2011-06-09 65 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