首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >NUnit 3,等待,死锁和UI

NUnit 3,等待,死锁和UI
EN

Stack Overflow用户
提问于 2016-06-20 17:38:19
回答 1查看 900关注 0票数 0

问题陈述

我们有一些测试,这些测试会导致在当前nunit线程上设置SynchronizationContext。据我所知,把这个和等待混合在一起会造成一种死锁。问题是,我们将业务逻辑与各地的UI关注点混合在一起。不太理想,但这不是我现在能轻易改变的。

代码示例

代码语言:javascript
复制
    [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线程在消息队列中有工作,必须排完后才能调度延续。因此,仅为测试目的,我插入了调用

代码语言:javascript
复制
    System.Windows.Forms.Application.DoEvents();

就在等待到来之前。奇怪的是:测试将不再是死锁,但是继续不会在以前的SynchronizationContext上执行,而是在线程池线程(SynchronizationContext.Current == null和不同的托管线程id)上执行!这很明显吗?本质上,添加这个调用看起来像'ConfigureAwait(false)‘。

有人知道为什么测试死锁吗?

假设它与nunit等待异步测试完成的方式有关,我认为我在一个单独的线程中运行了整个测试:

代码语言:javascript
复制
    [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线程上的代码(尽管它移除死锁)。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-06-20 18:59:15

代码语言:javascript
复制
// 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库

代码语言:javascript
复制
[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消息循环:

代码语言:javascript
复制
[Test]
public async Task MyTestMethod()
{
  await WindowsFormsContext.Run(async () =>
  {
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
    await Task.Delay(10);
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
  });
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/37928593

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档