nUnit Assert.That委托并发问题

时间:2014-03-05 18:07:58

标签: c# multithreading asynchronous nunit task-parallel-library

我的代码中遇到了一些临时死锁,无法绕过它。

简单代码(我无法创建一个简单的调用链来重现InvokeChangeEvent中的代码)

[Test]
public async void Test()
{
    sut.InvokeChangeEvent("./foo.file");
    // Event is handled by an async handler chaining multiple await resulting in a file write

    // await Task.Delay(3000);

    Assert.That(() => Directory.GetFiles("some dir").Count(), Is.EqualTo(3).After(15000, 300));

}

我知道y'所有(:D)都需要可执行代码,但我无法将其分解,因此我希望通过解释获得一些见解。

会发生什么:sut.InvokeChangeEvent调用事件处理程序,该处理程序稍后调用async事件处理程序,然后调用某些async。链的末尾产生Task.Run,归结为写3个文件。

以上Assert实现为After的委托,返回DelayedConstraint并且具有非常大的最长时间(15秒)和小的轮询间隔。

现在,当我调试代码时,InvokeChangeEvent调用完全执行到最后一个Task.Run但是当Task.Run返回时,执行被返回到主线程并且执行Assert进入"等待投票"。

然而,断言永远不会成功。当我调试问题时,在Assert委托运行(并且失败)后,始终会返回Task.Run

我已经想通了,当我在Assert之前放置一个await Task.Delay(3000);时,代码就会正确执行。

如上所述,受测试的系统有足够的等待和Task.Runs链接,我无法用一些简单的可运行代码重现该问题。

我已经谷歌搜索了一段时间,我无法弄清楚为什么Task.Run(在不同的线程上执行)会产生(临时)死锁,即使DelayedConstraint有一个显式轮询间隔,以允许主线程进展。

看起来DelayedConstraint通过某种Thread.Sleep锁定主线程。 await Task.Delay没有,我知道这一点。让我感到困惑的是,我已经检查过我总是做await(而且从不Task.Result等),因此希望文件在Assert执行之前编写。

(注意:Thread.Sleep代替await Task.Delay不起作用。)

通常DelayedConstraint用于确保文件系统正确编写了所有文件,因为我遇到了处理文件的文件系统的一些延迟。

我有一种感觉async void事件处理程序可能会造成我不理解的情况。

如果我设法创建一个简单的示例,我将更新该线程。

1 个答案:

答案 0 :(得分:3)

与VS2012单元测试类比,请尝试使用async Task签名而不是async void作为测试方法。这样,NUnit应该能够跟踪待处理的任务状态并通过Task.Exception检查异常。

根据定义,async void方法是一种即发即弃的概念。该方法立即返回(确切地说,在它内部的第一个异步await),然后没有办法处理它的完成或可能在其中抛出的任何错误。通常,async void方法are only good for event handlers提供了try/catch方法中的异常处理。