在Visual Studio 2010中运行单元测试时,AutoResetEvent过早发出信号
本文关键字:AutoResetEvent 信号 Studio Visual 2010 单元测试 运行 | 更新日期: 2023-09-27 18:29:39
我正在VS2010中运行一个MS单元测试,以测试我的多线程应用程序。
应用程序使用AutoResetEvent
来同步线程,声明如下:
private readonly AutoResetEvent captureParsedEvent = new AutoResetEvent(false);
主测试螺纹(ID:13)
主测试线程启动一个线程来解析捕获文件,然后在AutoResetEvent
上调用WaitOne()
,阻塞直到捕获完成:
int id = Thread.CurrentThread.ManagedThreadId;
CaptureManager.Instance.StartProcessingPackets();
Trace.WriteLine("[" + id + "]: WAITING ON CaptureParsedEvent");
captureParsedEvent.WaitOne();
Trace.WriteLine("[" + id + "]: WAITING ON CaptureParsedEvent DONE!");
// Analyse parsed capture...
(旁注:代码最初在WaitOne()
之后调用captureParsedEvent.Reset()
,但我在研究此问题时删除了它,因为我的研究得出结论,这可能对AutoResetEvent
对象没有必要。)
正在分析线程(ID:18)
同时,正在进行解析的线程向AutoResetEvent
发出这样的信号:
private void InstanceManagerStateChanged(ManagerStateEventArgs ea, object sender)
{
int id = Thread.CurrentThread.ManagedThreadId;
switch(ea.CurrentState)
{
case ManagerState.ReadPacketsDone:
Trace.WriteLine("'t[" + id + "]: CaptureParsedEvent SIGNAL");
captureParsedEvent.Set();
Trace.WriteLine("'t[" + id + "]: CaptureParsedEvent DONE!");
break;
}
}
通常情况下,一切都表现良好,我在输出窗口中看到以下预期输出:
[13]: WAITING ON CaptureParsedEvent
[18]: CaptureParsedEvent SIGNAL
[18]: CaptureParsedEvent DONE!
[13]: WAITING ON CaptureParsedEvent DONE!
然而,我间歇性地看到以下输出:
[13]: WAITING ON CaptureParsedEvent
[13]: WAITING ON CaptureParsedEvent DONE!
这显然给我带来了问题,因为捕获还没有真正完成解析。
上面的地方是captureParsedEvent.Set();
唯一出现的地方,所以我知道没有其他人在发出该事件的信号。
几个问题:
Trace.WriteLine()
线程是否安全并按正确顺序输出跟踪?我只在VS2010中运行单元测试时看到过这个问题——并行运行测试是否有什么有趣的事情,以及在这种情况下使用线程可能会导致问题?我的理解是测试是串行运行的,但不确定这是否正确。
您得到的似乎是由于在评估时ea.CurrentState
不是ManagerState.ReadPacketsDone
,所以它跳过了该case语句中的代码。ManualResetEvent
没有设置自己,如果他们设置了,那么对每个人来说都是一个巨大的问题(我从来没有听说过其他人有这样的问题),所以你只需要确保没有其他人设置事件。
问题1:Trace.WriteLine()
是线程安全的,但如果有多个线程调用写线,则不能保证这些调用会按顺序执行。但是,在您的情况下,SIGNAL
和DONE
消息将一个接一个地写入,因为它们是在同一个线程中执行的。更重要的是,如果您获得了正确的状态,那么至少CaptureParsedEvent SIGNAL
将在WAITING ON CaptureParsedEvent DONE
之前打印,因为它发生在您设置手动重置事件之前。发出信号后,不能保证WAITING ON CaptureParsedEvent DONE
和CaptureParsedEvent DONE
的打印顺序。
如果另一个线程同时在写,那么它可以在它们之间写一些东西。但正如我已经说过的:这很可能是由ea.CurrentState
不是ManagerState.ReadPacketsDone
引起的。
问题2:当你处理并发时,总是会有"有趣的事情"发生,或者它和你通常在并发编程中得到的一样"有趣":你只需要小心地线程。同样,我不认为您的问题来自并发,它只是看起来您没有处理正确的情况和/或其他人可以访问相同的ManualResetEvent
。
默认情况下,Visual Stufio 2010不会并行运行测试,您必须通过手动编辑测试设置文件(parallelExecutionCount=0)来启用此功能。
在查看您提供的代码时,罪魁祸首可能与信号是在singleton(CaptureManager.Istance)中完成的这一事实有关。可能存在以前的测试已经执行并且ManagerState已经完成的情况。试着自己运行测试来验证这个假设。
如果您按顺序运行测试,您可能需要在测试之间重置单例的状态,以避免类似的副作用。然而,请注意,在使用singleton的任何地方都必须这样做,如果在整个代码库中大量使用此类,这可能会被证明是不理想的。
如果您并行运行测试,那么所有的赌注都将落空,因为您无法保证在执行测试时处于已知状态。您最好的选择是重新设计对象之间的关系,以便在没有副作用的情况下实例化和执行图形。
我已经解决了我的问题。
事实证明,对于代码正在处理的每个数据包,它都将InstanceManagerStateChanged()
回调添加到委托列表中:
CaptureManager.Instance.ManagerStateChanged += InstanceManagerStateChanged;
但我们没有正确取消订阅,这意味着我可能收到了前一个数据包的通知。
在处理下一个数据包之前取消订阅修复了此问题:
CaptureManager.Instance.ManagerStateChanged -= InstanceManagerStateChanged;