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

mtk*_*nko 3 c# deadlock asynchronous task

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

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

OK - 12 - True 
6 - True
Run Code Online (Sandbox Code Playgroud)

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

Ste*_*ary 5

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

问题ConfigureAwait(false)是它必须应用于任何地方——对于await被调用方法的传递闭包中的每个。很容易错过一个,然后就会陷入僵局。例如,我上次检查时,HttpClient.GetAsync他们的一个移动平台版本缺少一个。即使您(以及您的所有依赖项)完美地做到了这一点,随着时间的推移,它也同样难以维护。

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

PS 如果您坚持阻塞,请使用GetAwaiter().GetResult()而不是Result,因为Result会将异常包装在AggregateException.