Reputation: 25287
I've got a method which looks like this:
static async Task<string> GetGooglePage(ProxyInfo proxy)
{
using (var m=new MyNetworkClass())
{
m.Proxy = proxy;
return await m.GetAsync("http://www.google.com");
}
}
now I want to call it from method that is not async, and get the result.
The way I was trying to do it is like this:
foreach (var proxy in proxys)
{
try
{
GetGooglePage(proxy.ToProxyInfo()).Wait();
}
catch
{}
lock (Console.Out)
{
Console.Write(current++ + "\r");
}
}
My problem is that sometimes GetGooglePage(proxy.ToProxyInfo()).Wait();
deadlocks (according to visual studio debugger, there is no stack beyond this call).
I don't want to use async all the way through to Main()
. How do I correctly call GetGooglePage
from synchronous code without risking a deadlock?
Upvotes: 1
Views: 1287
Reputation: 244757
The best option is not to do this: if you synchronously wait for an async method, there is no reason for the method to be async in the first place. So, what you should do (assuming you really want to or have to make the top level method synchronous) is to make all the methods synchronous.
If you can't do that, then you can prevent the deadlock by using ConfigureAwait(false)
:
static async Task<string> GetGooglePage(ProxyInfo proxy)
{
using (var m=new MyNetworkClass())
{
m.Proxy = proxy;
return await m.GetAsync("http://www.google.com").ConfigureAwait(false);
}
}
This way, when the method resumes, it won't try resuming on the UI thread, so you won't get the deadlock. If you're using await
in GetAsync()
or the methods it calls, you will need to do the same thing there too.
In general, it's a good idea to use ConfigureAwait(false)
everywhere, as long as you don't need to resume of the UI thread.
Upvotes: 2
Reputation: 456322
You're running into a deadlock that I describe on my blog.
There is no standard solution for blocking on an async
method, because there is no solution that works in every situation. The only officially recommended solution is to use async
all the way. Stephen Toub has a good summary of workarounds here.
One approach is to execute the async
method in a background thread (via Task.Run
) and then Wait
on that. Disadvantages of this approach are: 1) it won't work for async
methods that require a specific context (e.g., write to an ASP.NET response, or update a UI); 2) changing from a synchronized context to the unsynchronized thread pool context may introduce race conditions; and 3) it burns a thread just waiting for another thread.
Another approach is to execute the async
method within a nested message loop. Disadvantages of this approach are: 1) the "nested message loop" is different for every platform (WPF vs. WinForms, etc); and 2) nested loops introduce reentrancy issues (which have been responsible for many, many bugs in the Win32 era).
Upvotes: 4