问题陈述
我们有一些测试,这些测试会导致在当前nunit线程上设置SynchronizationContext。据我所知,把这个和等待混合在一起会造成一种死锁。问题是,我们将业务逻辑与各地的UI关注点混合在一起。不太理想,但这不是我现在能轻易改变的。
代码示例
[Test]
public async Task DeadLock()
{
// force the creation of a SynchronizationContext
var form = new Form1();
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
await Task.Delay(10);
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
}此测试将死锁(.NET 4.6.1)。我不知道为什么。我的假设是,“变成”UI线程的nunit线程在消息队列中有工作,必须排完后才能调度延续。因此,仅为测试目的,我插入了调用
System.Windows.Forms.Application.DoEvents();就在等待到来之前。奇怪的是:测试将不再是死锁,但是继续不会在以前的SynchronizationContext上执行,而是在线程池线程(SynchronizationContext.Current == null和不同的托管线程id)上执行!这很明显吗?本质上,添加这个调用看起来像'ConfigureAwait(false)‘。
有人知道为什么测试死锁吗?
假设它与nunit等待异步测试完成的方式有关,我认为我在一个单独的线程中运行了整个测试:
[Test]
public void DeadLock2()
{
Task.Run(
async () =>
{
// force the creation of a SynchronizationContext
var form = new Form1();
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
//System.Windows.Forms.Application.DoEvents();
await Task.Delay(10);
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
}).Wait();
}但这并不能解决问题。“等待”永远不会回来。请注意,我不能使用ConfigureAwait(false),因为连续性中有需要在UI线程上的代码(尽管它移除死锁)。
发布于 2016-06-20 18:59:15
// force the creation of a SynchronizationContext
var form = new Form1();我相信这会在当前版本的WinFormsSynchronizationContext中安装一个WinForms,但是要注意,在以前的版本中,这是行不通的。过去,您必须在安装SyncCtx之前创建一个实际的控件句柄。
我的假设是,“变成”UI线程的nunit线程在消息队列中有工作,必须排完后才能调度延续。
实际上,对于UI上下文,延续本身封装在一个委托中,该委托作为一种特殊的消息类型发布到消息队列中。如果UI消息循环没有运行,则根本无法执行它。
奇怪的是:测试将不再是死锁,但是继续不会在以前的SynchronizationContext上执行,而是在线程池线程(SynchronizationContext.Current == null和不同的托管线程id)上执行!这很明显吗?
太奇怪了。我不知道为什么会这样。
我以为我在一个单独的线程里运行整个测试..。但这并不能解决问题。
不,因为消息循环也不在该线程上运行。
ConfigureAwait(假).移除死锁
是的,因为它调度线程池线程上的继续,而不是将其排队到UI消息循环中。
问题是,我们将业务逻辑与各地的UI关注点混合在一起。不太理想,但这不是我现在能轻易改变的。
如果单线程上下文充分解决了您的"UI关注点“,则可以在AsyncContext中使用我的AsyncEx库。
[Test]
public void MyTestMethod()
{
AsyncContext.Run(async () =>
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
await Task.Delay(10);
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
});
}AsyncContext提供了自己的单线程SynchronizationContext,并运行了各种类型的“主循环”,但它不是Win32消息循环,也不足以实现STA互操作。
如果您的"UI关注点“特别依赖于WinForms上下文(即,您的代码假定存在一个Win32消息泵,使用STA对象或其他什么),那么您可以使用(最初作为异步CTP的一部分分发),它使用真实的WinFormsSynchronizationContext并泵出一个真实的Win32消息循环:
[Test]
public async Task MyTestMethod()
{
await WindowsFormsContext.Run(async () =>
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
await Task.Delay(10);
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
});
}https://stackoverflow.com/questions/37928593
复制相似问题