【发布时间】:2016-12-23 19:20:49
【问题描述】:
关于如何避免从同步代码(例如 this)调用的异步代码(例如,HttpClient 方法)中的死锁,关于 SO 存在许多问题。我知道避免这些死锁的各种方法。
相比之下,我想了解在测试期间加剧或触发错误代码中这些死锁的策略。
这是最近给我们带来问题的一些错误代码示例:
public static string DeadlockingGet(Uri uri)
{
using (var http = new HttpClient())
{
var response = http.GetAsync(uri).Result;
response.EnsureSuccessStatusCode();
return response.Content.ReadAsStringAsync().Result;
}
}
它是从 ASP.NET 应用程序调用的,因此具有非null 值SynchronizationContext.Current,这为潜在的死锁火灾提供了燃料。
除了blatantly misusing HttpClient,这段代码在我们公司的一台服务器上死锁了……但只是偶尔发生。
我尝试重现死锁
我在 QA 部门工作,所以我尝试通过一个单元测试来重现死锁,该单元测试命中 Fiddler 的侦听器端口的本地实例:
public class DeadlockTest
{
[Test]
[TestCase("http://localhost:8888")]
public void GetTests(string uri)
{
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
var context = SynchronizationContext.Current;
var thread = Thread.CurrentThread.ManagedThreadId;
var result = DeadlockingGet(new Uri(uri));
var thread2 = Thread.CurrentThread.ManagedThreadId;
}
}
需要注意的几点:
默认情况下,单元测试有一个空的
SynchronizationContext.Current、and so.Resultcaptures the context ofTaskScheduler, which is the thread pool context。因此,我使用SetSynchronizationContext将其设置为特定上下文,以更接近地模拟 ASP.NET 或 UI 上下文中发生的情况。我已将 Fiddler 配置为在响应之前等待一段时间(约 1 分钟)。我从同事那里听说这可能有助于重现僵局(但我没有确凿的证据)。
我已经用调试器运行它以确保
context不是null和thread == thread2。
不幸的是,我在这个单元测试中没有触发死锁。无论 Fiddler 中的延迟有多长,它总是会结束,除非延迟超过 HttpClient 的默认 100 秒 Timeout(在这种情况下,它只会出现异常)。
我是否缺少点燃僵局之火的成分?我想重现死锁,只是为了肯定我们的最终修复确实有效。
【问题讨论】:
标签: c# async-await deadlock dotnet-httpclient qa