简体   繁体   中英

How to safely wait for async method?

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?

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 (eg, 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).

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM