2011-06-09 62 views
3

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

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. 集()

这是我有时看到我的日志:

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

不应该'OpenDevice'方法指定设备何时打开(而不是'TestDevice'方法)? – 2011-06-09 16:55:16

+0

OpenDevice是一个异步方法调用。 TestDevice在设备上执行一系列操作,例如打开,锁定,打开电源,关闭电源。 – Robert 2011-06-09 16:59:49

回答

2

由于OpenDevice是异步的(如你在留言中提到),它在不同的线程比它的调用运行。有时在源下一行执行前,将完成:

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

当发生这种情况DeviceOpenedEvent不会做你希望它是什么,因为WaitForEvent仍然EventEnum.None

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

的解决方案是更改您的代码,以便在保证以正确顺序运行的方法中发出完成信号。这里有一个简单的实现,消除枚举并使用一个等待句柄每次需要等待事件:只需拨打deviceOpened.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那就更简单了。您甚至可以更改OpenDevice以接受自动重置事件并在TestDevice之内构建它,这将减少您面临多线程错误的风险。

+0

这种情况不可能如何? 1. OpenDevice()2.异步回调3. deviceOpenedEvent.WaitOne() – Robert 2011-06-09 20:43:01

+0

这种情况是可能的,但它不是问题。当它发生时,'deviceOpenedEvent.WaitOne'不会被阻塞(就像弗雷德里克在他的回答中解释的那样),因为'AutoResetEvent'保持发送信号,直到'消耗'它为止。除非有另一个线程在等待事件,否则没关系(你可以通过使用'ManualResetEvent'或其他信号机制来解决*)。 – 2011-06-09 20:49:58

1

这不应该是一个问题。该documentationAutoResetEvent状态:

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

下面的代码不会导致WaitOne阻拦,例如:

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

它不会阻塞 - 除非有另一个线程正在等待同一个事件,在这种情况下,另一个线程将消耗信号并且事件将被重置。 @罗伯特,是这样的吗? – 2011-06-09 17:07:19

+0

我更新了问题来详细说明问题 – Robert 2011-06-09 17:19:15

+0

@Robert:这个更新听起来好像@Jeff描述的情况可能是有效的。有没有其他的代码使用'WaitOne'来等待同一个'AutoResetEvent'实例中的信号? – 2011-06-09 17:25:44