简体   繁体   English

nUnit Assert。那个委托并发问题

[英]nUnit Assert.That delegate concurrency issue

I am experiencing some temporary dead lock in my code and can't wrap my head around it. 我在代码中遇到了一些暂时的死锁,无法解决问题。

Simple code (I cannot create a simple call chain to reproduce the code in InvokeChangeEvent ) 简单代码(我无法创建简单的调用链来重现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));

}

I am aware that y'all (:D) want executable code but I wasn't able to slice it down therefore I hope for some insight by explanation. 我知道你们所有人(:D)都需要可执行代码,但我无法将其分解,因此我希望通过解释获得一些见识。

What happens: sut.InvokeChangeEvent calls an event handler that later calls an async event handler which then calls some async . 发生了什么: sut.InvokeChangeEvent调用一个事件处理程序,然后再调用一个async事件处理程序,然后再调用一些async The end of the chain results in an Task.Run that boils down to write 3 files. 链的末尾将产生一个Task.Run ,最终可以写入3个文件。

The Assert above is implemented as a delegate with After that returns a DelayedConstraint and has a very large max time (15 secs) and a small polling interval. 上面的Assert被实现为带有After的委托,该After返回DelayedConstraint并且具有很大的最大时间(15秒)和很小的轮询间隔。

Now when I debug the code the InvokeChangeEvent call is entirely executed to the last Task.Run but when the Task.Run returns, the execution is yielded back to the main thread and the Assert is executed entering the "wait with polling". 现在,当我调试代码时,将对最后一个Task.Run完全执行InvokeChangeEvent调用,但是当Task.Run返回时,将执行返回给主线程,并执行Assert,进入“等待轮询”。

However the assert never succeeds. 但是断言永远不会成功。 When I debug the issue the return of the Task.Run is always handled after the Assert delegate has run (and failed). 当我调试问题时,始终 Assert委托运行(并失败) 之后处理Task.Run的返回。

I've figured out, that when I place an await Task.Delay(3000); 我已经知道,当我放置一个await Task.Delay(3000); before the Assert, then the code executes properly. 在声明之前,代码将正确执行。

As mentioned the system under test has plenty await and Task.Runs chained and I was unable to reproduce the issue with some easy runnable code. 如前所述,被测系统有大量等待和Task.Runs链接在一起,我无法通过一些简单的可运行代码重现该问题。

I've been googling around for a while and I cannot figure out why the Task.Run (which is executed on a different thread) yield in a (temporary) deadlock even though the DelayedConstraint has an explicit polling interval to allow the main thread to progress. 我已经搜索了一段时间,但我无法弄清为什么Task.Run(在不同线程上执行)会导致(临时)死锁,即使DelayedConstraint具有明确的轮询间隔以允许主线程执行进展。

It looks like the DelayedConstraint locks the main thread by some sort of Thread.Sleep . 看起来DelayedConstraint通过某种Thread.Sleep锁定了主线程。 await Task.Delay does not, I am aware of that. await Task.Delay没有,我知道这一点。 What confuses me is I have checked that I always do an await (and never Task.Result , etc) and therefore would expect that the file has been written before the Assert has executed. 令我感到困惑的是,我检查了我一直在await (而不是Task.Result等),因此希望在执行Assert之前已写入文件。

(Note: Thread.Sleep instead of await Task.Delay does not work.) (注意: Thread.Sleep而不是await Task.Delay无效。)

Usually the DelayedConstraint is used to ensure that file system has properly written all files as I have experienced some delays of the file system dealing with files. 通常, DelayedConstraint用于确保文件系统正确写入了所有文件,因为我遇到了文件系统处理文件的一些延迟。

I have some feeling that async void event handler may create a situation which I do not understand. 我感到async void事件处理程序可能会导致我不了解的情况。

If I manage to create a simple sample, I will update the thread. 如果我设法创建一个简单的示例,则将更新该线程。

By analogy with VS2012 unit testing, try async Task signature rather than async void for your test method. 与VS2012单元测试类似,请尝试将async Task签名而不是async void用于您的测试方法。 This way, NUnit should be able to keep track of the pending task status and inspect exceptions via Task.Exception . 这样,NUnit应该能够跟踪待处理的任务状态并通过Task.Exception检查异常。

The async void method is a fire-and-forget concept, by definition. 根据定义, async void方法是一劳永逸的概念。 The method returns instantly (precisely, upon the first asynchronous await inside it), and then there is no way to handle its completion or any errors possibly thrown inside it. 该方法立即返回(恰好在内部第一次异步await时返回),然后无法处理其完成或其中可能引发的任何错误。 As is, async void methods are only good for event handlers , provided exceptions are handled within the method with try/catch . async voidasync void方法仅适用于事件处理程序 ,前提是使用try/catch在方法内处理异常。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM