【问题标题】:Deadlock while blocking async HttpClient call within a task continuation在任务继续中阻塞异步 HttpClient 调用时出现死锁
【发布时间】:2015-03-05 16:51:30
【问题描述】:

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

在下面的示例中,我阻止了一个内部使用 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);
        }
    }

你能解释一下这两个电话有什么区别吗?

【问题讨论】:

  • 不要同步阻塞异步方法。时期。几乎总是做错事。 await 点击处理程序中的方法。

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


【解决方案1】:

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

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

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

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

至于修复,您首先不应该同步等待异步操作。要么让整个事情异步,要么让所有事情同步。

【讨论】:

  • 实际上这个延续确实会运行。死锁的是延续中的 GetAsync。如果我实际上通过Task.Run 启动任务,您描述的行为是有效的,例如Task.Run(() =&gt; true),在这种情况下另一个线程池线程返回true。但是,在使用 Task.FromResult 的示例中,继续运行但 GetAsync 死锁。我一直同意 async 但我只是想了解为什么第一个示例有效而第二个无效。
  • @AlexK 不,这不是真的。您将当前上下文传递给ContinueWith 调用,因此虽然延续将立即调度,但它不会真正开始执行,直到同步上下文实际执行该方法。在它正在抽取的当前消息(单击事件处理程序)完成之前,它将无法执行该方法。正如我所说,GetAsync 根本不涉及死锁。
  • 据我在 WPF 应用程序上调试它所见,这是真的。使用Task.FromResult(true) 的第一个代码sn-p 在var example2 = GetAsync(uri).Result 上中断,然后死锁。如果我用Task.Run(() =&gt; true) 替换Task.FromResult(true),则应用程序死锁而无法继续。你能解释一下这两段代码之间的区别吗?无论如何,我接受您的回答,因为您为我指明了正确的方向。谢谢
猜你喜欢
  • 1970-01-01
  • 2019-01-13
  • 1970-01-01
  • 1970-01-01
  • 2018-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多