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

Call async method from sync action methods: Task.Run or ConfigureAwaits(false)

我可以防止 Result 在控制器的同步操作方法中调用异步任务的死锁,同时使用:ConfigureAwaits(false) on Task或使用 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;
        }
    }
}

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

OK - 12 - True 
6 - True

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

sync-over-async 没有通用的解决方案,只有各种反模式,每个反模式都有不同的问题。详细信息见我的MSDN article on brownfield async.

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

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

上的可伸缩性问题

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