在任务延续期间阻止异步HttpClient调用时死锁

时间:2015-01-07 17:47:56

标签: 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);
        }
    }

请解释一下这两个电话有什么区别?

1 个答案:

答案 0 :(得分:5)

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

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

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

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

至于修复,你不应该首先同步等待异步操作。要么使整个事物异步,要么使它全部同步。