jus*_*mer 0 .net multithreading unit-testing blocking
我有一个在指定时间后引发事件的类(它使用System.Timers.Timer内部).在我的测试代码中,我创建了一个Stopwatch我在创建类之前启动的,并设置了事件的回调来停止它Stopwatch.然后,我阻止直到Not Stopwatch.IsRunning.简单吧?
我原来的阻止代码是
While Stopwatch.IsRunning
End While
Run Code Online (Sandbox Code Playgroud)
但我发现像这样的空循环永远不会让我的回调发射!一旦我将调试代码放入while循环中,就可以了!
Dim lastSecond As Integer = 0
While sw.IsRunning
If (Date.Now.Second > lastSecond) Then
lastSecond = Date.Now.Second
Console.WriteLine("blocking...")
End If
End While
Run Code Online (Sandbox Code Playgroud)
是什么导致了这种奇怪的行为,更重要的是,我可以在阻塞部分中放入哪些最简单的代码来启动事件?
While Stopwatch.IsRunning
End While
Run Code Online (Sandbox Code Playgroud)
它是线程中的大罪之一,被称为"热等待循环".线程有很多罪,其中很多都没有黄带,但这一点特别阴险.主要问题是你让一个处理器核心炽热,在紧密循环中测试IsRunning属性.
当您使用x86抖动时,这会产生一个非常讨厌的问题,它会在发布版本中生成代码,该代码读取cpu寄存器中的IsRunning属性支持字段变量.并反复测试cpu寄存器值,而无需从字段重新加载值.这是最终的死锁,它永远不会退出循环.您通过编辑代码或使用调试器将其从该模式中删除.为了避免它,属性的支持字段必须声明为volatile,但这不是你可以在VB.NET中做的事情,也不是正确的修复.
相反,你应该使用一个适当的同步对象,一个让你发信号通知另一个线程发生的事情.一个很好的是AutoResetEvent,你可以像这样使用它:
Dim IsCompleted As New AutoResetEvent(False)
Private Sub WaitForTimer()
IsCompleted.WaitOne()
''etc..
End Sub
Private Sub timer_Elapsed(ByVal sender As Object, ByVal e As EventArgs) Handles timer.Elapsed
IsCompleted.Set()
timer.Stop()
End Sub
Run Code Online (Sandbox Code Playgroud)
请注意,AutoResetEvent也缺少黄色磁带.在另一个线程尚未调用WaitOne()的情况下多次调用Set()结果很糟糕.