0

我試圖確定我是否可以在這裏使用ManualResetEvent以確保在併發環境中,myMethod()的內部操作是從不調用ManualResetEvent - 這裏會出現競爭狀況嗎?

static volatile bool _syncInitialized = false; 

    static ManualResetEvent _syncEvent = new ManualResetEvent(false); 

    static object _syncLock = new object(); 

    void myMethod() 
    { 
     lock (_syncLock)  
     { 
      if (!_syncInitialized)   // sync hasn't started, so 
      { 
       _syncInitialized = true;  
       _syncEvent.Set();   // signal for the first time only 
      } 
     } 

     if (_syncEvent.WaitOne())   // wait for signal 
     { 
      _syncEvent.Close(); 
      _syncEvent = new ManualResetEvent(false); // reset to false 

      // actions that should be forced to run sequentially 
     } 
    } 

編輯 - 請注意,我使用的ManualResetEvent而不是隻鎖定(),因爲我希望能夠增加超時,有可能。

+0

但鎖應該一次運行您的代碼一個線程,爲什麼使用ManualResetEvent? – Christian

+3

MRE只對信號有用,它不**提供排除。使用* lock *關鍵字,Mutex或Semaphore。 –

+0

@基督教,好問題。如有必要,使用ManualResetEvent,以便在WaitOne()上使用超時。 –

回答

3

您至少有一個競爭條件的機會。考慮:

線程#1執行_syncEvent.WaitOne()併成功,然後在它可以執行_syncEvent.Close()之前被換出。線程#2出現並執行WaitOne(),並且也成功。

另一個問題是,你打電話Close(),然後構建一個新的實例,顯然作爲一種重置的方式。試想一下,你叫Close(),線程被換出,下一個線程出現,並嘗試執行WaitOne(),並拋出異常,因爲該對象已關閉。

如果要重置事件,請致電Reset()

您可能無法使用ManualResetEvent進行此項工作。正如其他人所說,ManualResetEvent用於信號發送,而不是互斥。

你說你會想在未來實現超時。如果您只想讓一個線程在一段時間內等待鎖定,然後在無法獲取鎖定的情況下退出,請使用接受超時值的Monitor.TryEnter重載之一。例如:

private object _syncObject = new Object(); 
void MyMethod() 
{ 
    if (!Monitor.TryEnter(_syncObject, TimeSpan.FromSeconds(5))) 
    { 
     return; // couldn't get the lock 
    } 
    try 
    { 
     // got the lock. Do stuff here 
    } 
    finally 
    { 
     Monitor.Exit(); // release the lock 
    } 
} 

有超過您是否真的要以釋放finally鎖定一些爭論。如果代碼拋出異常,那麼可能(有可能?)您正在保護的資源現在處於不完整或其他損壞狀態。在這種情況下,您可能不想讓其他線程對其執行操作。無論您在異常情況下是否釋放鎖,都需要根據應用程序的需求做出設計決策。