从同步操作方法调用异步方法:Task.Run或ConfigureAwaits(false)

时间:2016-10-30 12:56:35

标签: c# asynchronous task deadlock

我可以使用以下两者: ConfigureAwaits(false)或使用 Task.Run阻止结果调用async任务同步操作控制器中的死锁即可。在这两种情况下,异步方法都将在线程池中的线程上完成。控制器来源:

public class TestController : Controller
{
    /// <summary>
    /// Thread from threadpool will be used to finish async method.
    /// </summary>
    public string TaskRun()
    {
        return Task.Run(GetStatus).Result + " <br/> " +
                Thread.CurrentThread.ManagedThreadId + " - " +
                Thread.CurrentThread.IsThreadPoolThread;
    }

    /// <summary>
    /// After completion it will try to finish async method in original thread used to work for httprequest.
    /// </summary>
    public string Deadlock()
    {
        return GetStatus().Result;
    }

    /// <summary>
    /// Thread from threadpool will be used to finish async method.
    /// </summary>
    public string AwaitsFalse()
    {
        return GetStatusAwaitsFalse().Result + " <br/> " +
                Thread.CurrentThread.ManagedThreadId + " - " +
                Thread.CurrentThread.IsThreadPoolThread;
    }

    public async Task<string> PureAsync()
    {
        return await GetStatus() + " <br/> " +
                Thread.CurrentThread.ManagedThreadId + " - " +
                Thread.CurrentThread.IsThreadPoolThread;
    }

    public static async Task<string> GetStatusAwaitsFalse()
    {
        using (var httpClient = new HttpClient())
        {
            var response = await httpClient.GetAsync("http://www.google.com")
                .ConfigureAwait(false);
            return response.StatusCode + " - " + 
                Thread.CurrentThread.ManagedThreadId + " - " +
                Thread.CurrentThread.IsThreadPoolThread;
        }
    }

    public static async Task<string> GetStatus()
    {
        using (var httpClient = new HttpClient())
        {
            var response = await httpClient.GetAsync("http://www.google.com");
            return response.StatusCode + " - " +
                Thread.CurrentThread.ManagedThreadId + " - " +
                Thread.CurrentThread.IsThreadPoolThread;
        }
    }
}

/ test / taskrun 的输出(最后两个int值是ManagedThreadId和IsThreadPoolThread):

OK - 12 - True 
6 - True

/ test / awaitsfalse 的输出是相同的。有什么不同吗?

1 个答案:

答案 0 :(得分:3)

对于异步同步,没有通用的解决方案,只有各种各样的反模式都有不同的问题。有关详细信息,请参阅我的MSDN article on brownfield async

ConfigureAwait(false)的问题在于它必须在无处不在 - 对于被调用方法的传递闭包中的每个await。它真的很容易 错过一个,然后你就会等待发生死锁。例如,我上次检查时,HttpClient.GetAsync缺少一个移动平台版本。即使您(以及所有依赖项)完成完美,它也会随着时间的推移维护一样难。

Task.Run的问题在于它在线程池线程上运行代码。 (明显)。这对于可以在线程池线程上运行的代码来说很好,但对于所有代码而言并非如此。它的效率也较低,(如果过度使用)会导致ASP.NET出现可伸缩性问题。

P.S。如果您坚持要阻止,请使用GetAwaiter().GetResult()代替Result,因为Result会将异常包装在AggregateException中。