0

我在 VS2010 中运行 MS 单元测试来测试我的多线程应用程序。

应用程序使用 anAutoResetEvent来同步线程,它的声明如下:

private readonly AutoResetEvent captureParsedEvent = new AutoResetEvent(false);

主测试线程(ID:13)

主测试线程启动一个线程来解析一个捕获文件,然后调用WaitOne()一个AutoResetEvent,阻塞直到捕获完成:

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...

旁注:代码最初调用了captureParsedEvent.Reset()afterWaitOne()但我在调查此问题时删除了它,因为我的研究得出结论,这对于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();所以我知道没有其他人在发出这个事件的信号。

几个问题:

  1. Trace.WriteLine()线程安全并以正确的顺序输出跟踪吗?

  2. 我只在 VS2010 中运行单元测试时才看到这个问题——并行运行的测试是否有一些有趣的事情发生,并且在这种情况下使用线程可能会导致问题?我的理解是测试是串行运行的,但不确定这是否正确。

4

3 回答 3

1

似乎您得到的结果是由于在评估时ea.CurrentStateis not引起的ManagerState.ReadPacketsDone,因此它跳过了该 case 语句中的代码。ManualResetEvents 不设置自己,如果他们设置了,那么这对每个人来说都是一个巨大的问题(我从未听说过其他人有这样的问题),所以你只需要确保没有其他人在设置事件。

问题 1:Trace.WriteLine()是线程安全的,但是如果您有多个线程调用 writeline,则不能保证这些调用将按顺序执行。但是,在您的情况下,SIGNALandDONE消息将一个接一个地写入,因为它们是在同一个线程中执行的。更重要的是,如果您获得了正确的状态,那么至少CaptureParsedEvent SIGNAL会在之前打印,WAITING ON CaptureParsedEvent DONE因为它发生在您设置手动重置事件之前。发出信号后,无法保证WAITING ON CaptureParsedEvent DONEand的打印顺序CaptureParsedEvent DONE

如果另一个线程同时在写,那么它可以在它们之间写一些东西。但正如我已经说过的:这很可能是由ea.CurrentStatenot being引起的ManagerState.ReadPacketsDone

问题 2:当你处理并发时,总是有“有趣的事情”发生,或者它和你通常在并发编程中得到的一样“有趣”:你只需要小心线程。同样,我不认为您的问题来自并发,只是看起来您没有处理正确的案例和/或其他人可以访问相同的ManualResetEvent.

于 2012-03-30T04:30:59.053 回答
1

默认情况下,Visual Stufio 2010 不会并行运行测试,您必须通过手动编辑测试设置文件 (parallelExecutionCount=0) 来启用它。

在查看您提供的代码时,罪魁祸首可能与信号是在单例 (CaptureManager.Instance) 内完成的事实有关。可能存在之前的测试已经执行并且 ManagerState 已经完成的情况。尝试自行运行测试以验证此假设。

如果您按顺序运行测试,您可能需要在测试之间重置单例的状态以避免这样的副作用。但是请注意,在使用单例的任何地方都必须这样做,如果在整个代码库中大量使用此类,则可能证明不太理想。

如果您正在并行运行测试,那么所有的赌注都将被取消,因为您无法保证测试执行时的一个众所周知的状态。您最好的选择是重新设计对象之间的关系,以便可以实例化和执行图形而不会产生副作用。

于 2012-04-01T15:24:15.647 回答
0

我已经解决了我的问题。

事实证明,对于代码正在处理的每个数据包,它都将InstanceManagerStateChanged()回调添加到委托列表中:

CaptureManager.Instance.ManagerStateChanged += InstanceManagerStateChanged;

但我们没有正确取消订阅,这意味着我可能已经收到了来自前一个数据包的通知。

在处理下一个数据包之前取消订阅修复了这个问题:

CaptureManager.Instance.ManagerStateChanged -= InstanceManagerStateChanged;
于 2012-04-16T02:11:40.003 回答