c# - 在任务继续中阻止异步 HttpClient 调用时出现死锁

标签 c# async-await task deadlock dotnet-httpclient

我正在努力解决同步调用异步函数和死锁的问题。

在下面的示例中,我阻止了内部使用 ConfigureAwait(false) 的异步调用,据我所知应该可以防止死锁。对 Web 服务的第一次调用不会死锁。但是,第二个发生在同步回 UI 线程死锁的延续中。

GetBlocking_Click 是 WPF 应用程序中的点击事件,因此第一个和第二个请求都发生在 UI 线程上。

      private void GetBlocking_Click(object sender, RoutedEventArgs e)
    {
        const string uri = "http://www.mywebservice.com";
        var example1 = GetAsync(uri).Result;

        Task.FromResult(true)
            .ContinueWith(t =>
            {
                var example2 = GetAsync(uri).Result;
            }, 
            TaskScheduler.FromCurrentSynchronizationContext()).Wait();
    }

    private async Task<string> GetAsync(string url)
    {
        using (var client = new HttpClient())
        {
            var responseMessage = await client.GetAsync(url).ConfigureAwait(false);

            return await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
        }
    }

您能解释一下这两个调用之间的区别吗?

最佳答案

您在 UI 线程中。从该线程调用 Task.FromResult 并创建一个任务。然后向需要在 UI 线程中运行的任务添加延续。该延续被添加到消息泵处理的队列中。

然后您等待该延续在 UI 线程中完成。该延续正在等待 UI 可用,以便甚至启动延续的主体,这将最终调用 GetAsync。这是一个僵局。

continuation 的主体是什么并不重要;出于同样的原因,以下代码死锁:

private void GetBlocking_Click(object sender, RoutedEventArgs e)
{
    Task.FromResult(true)
        .ContinueWith(t =>{ }, 
            TaskScheduler.FromCurrentSynchronizationContext())
        .Wait();
}

至于修复,您只是不应该首先同步等待异步操作。要么使整个事情异步,要么使其全部同步。

关于c# - 在任务继续中阻止异步 HttpClient 调用时出现死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27825467/

相关文章:

c# - 将 SQL Server 数据库添加到 Visual Studio 2013 ASP.Net 网页

react-native - React Native 异步等待调度存储

c# - 在不同时间需要返回值时将任务链接在一起的正确方法 #2

c# - 如何将类中的字符串连接到表单

c# - 单个单词的正则表达式

async-await - 如何在捕获的 ExecutionContext 上运行异步委托(delegate)

c# - 如何捕获从延续抛出的未处理异常?

c# - 你如何使用 C# NetworkStream ReadAsync() 只抓取 stx 和 etx 之间的东西?

c# - 如何从偏移量开始并遍历整个列表?

javascript - ES2017 - 异步与 yield