同步事件

时间:2011-06-09 16:44:52

标签: c# .net multithreading concurrency

我注意到如果事件发生得太快,我的代码有时会变得不同步。我想知道是否有更好的方法。在正常情况下,我在TestDevice方法中将线程告诉WaitOne后会触发DeviceOpenedEvent,但在某些情况下,我发现事件在线程有机会等待之前被触发。

    protected AutoResetEvent TestAutoResetEvent = new AutoResetEvent(false);
    public EventEnum WaitForEvent = EventEnum.None;

    bool TestDevice()
    {
        OpenDevice();

        WaitForEvent = EventEnum.DeviceOpened;
        TestAutoResetEvent.WaitOne();
        WaitForEvent = EventEnum.NoWait;

        //Continue with other tests
    }

    void DeviceOpenedEvent()
    {
        if (WaitForEvent == EventEnum.DeviceOpened)         
            TestAutoResetEvent.Set();                           
    }

在正常情况下,它看起来像这样:

  1. 打开设备
  2. WaitOne的()
  3. 发生DeviceOpenedEvent
  4. 集()
  5. 这就是我有时会看到我的日志:

    1. 打开设备
    2. 发生DeviceOpenedEvent
    3. WaitOne()基本上永远停留在这里

2 个答案:

答案 0 :(得分:2)

由于OpenDevice是异步的(正如您在注释中所提到的),因此它在与调用者不同的线程中运行。有时它会在源代码执行的下一行之前完成:

    OpenDevice(); // Async: may finish before the next line executes!
    WaitForEvent = EventEnum.DeviceOpened;

当发生这种情况时DeviceOpenedEvent无法满足您的要求,因为WaitForEvent仍然是EventEnum.None

if (WaitForEvent == EventEnum.DeviceOpened)         
    TestAutoResetEvent.Set(); 

解决方案是更改代码,以便在保证以正确顺序运行的方法内发出完成信号。这是一个简单的实现,它删除枚举并为您需要等待的每个事件使用一个等待句柄:

protected AutoResetEvent deviceOpenedEvent = new AutoResetEvent(false);
protected AutoResetEvent deviceLockedEvent = new AutoResetEvent(false);

bool TestDevice() {
    OpenDevice();
    // Do some unrelated parallel stuff here ... then
    deviceOpenedEvent.WaitOne();
    LockDevice();
    deviceLockedEvent.WaitOne();
}

void DeviceOpenedEvent() {
    deviceOpenedEvent.Set();                           
}

如果您控制OpenDevice,则更容易:只需在完成后调用deviceOpened.Set()即可。您甚至可以更改OpenDevice以接受自动重置事件并在TestDevice内构建它,这将减少您对多线程错误的曝光。

答案 1 :(得分:1)

这应该不是问题。 AutoResetEvent状态的documentation

  

如果一个线程在调用WaitOne时   AutoResetEvent处于信号状态   状态,线程不会阻塞。

以下代码不会导致WaitOne阻止,例如:

AutoResetEvent waitHandle = new AutoResetEvent(false);
waitHandle.Set();
waitHandle.WaitOne();
Console.WriteLine("After WaitOne");