在任务继续中阻止异步 HttpClient 调用时出现死锁
Deadlock while blocking async HttpClient call within a task continuation
我正在努力解决同步调用异步函数和死锁问题。
在下面的示例中,我阻止了内部使用 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();
}
至于修复,您首先不应该同步等待异步操作。要么使整个事情异步,要么使它全部同步。
我正在努力解决同步调用异步函数和死锁问题。
在下面的示例中,我阻止了内部使用 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();
}
至于修复,您首先不应该同步等待异步操作。要么使整个事情异步,要么使它全部同步。