2011-03-21 28 views
4

下面的方法應該對第一個調用返回true,對於其他調用請返回false。我應該使用ManualResetEvent作爲鎖對象嗎?

它有什麼問題嗎?使用重置事件進行鎖定安全嗎?

private ManualResetEvent _resetEvent = new ManualResetEvent(false); 

public bool AmIFirst() 
{ 
    lock (_resetEvent) 
    { 
     bool first = !_resetEvent.WaitOne(0); 
     if (first) 
      _resetEvent.Set(); 

     return first; 
    } 
} 

編輯:我作了發言,回顧你的言論後的一些變化。由於以前的設計理念,我被困在ManualResetEvent。我其實根本不需要它。

class ActionSynchronizer 
{ 
    private Timer _expirationTimer; 
    private object _locker = new object(); 
    private bool _executionRequired = true; 

    private SomeDelegate _onExpired = delegate { }; 

    public ActionSynchronizer(SomeDelegate onExpired) 
    { 
     _onExpired = onExpired; 
     expirationTimer = new Timer(OnExpired, null, 30000, Timeout.Infinite); 
    } 

    public bool IsExecutionRequired() 
    { 
     if (!_executionRequired) 
      return false; 

     lock (_locker) 
     { 
      if (_executionRequired) 
      { 
       _executionRequired = false; 
       return true; 
      } 

      return false; 
     } 
    } 

    private void OnExpired(object state) 
    { 
     if (_executionRequired) 
     { 
      lock (_locker) 
      { 
       if (_executionRequired) 
       { 
        _executionRequired = false; 
        // http://stackoverflow.com/questions/1712741/why-does-asynchronous-delegate-method-require-calling-endinvoke/1712747#1712747 
        _onExpired.BeginInvoke(_originalAction, EndInvoke, null); 
       } 
      } 
     } 
    } 
} 

// ... 
{ 
    if (_action.Sync.IsExecutionRequired()) 
     _action.Invoke(); 
} 

回答

13

我會去這裏不同的路線......

private int counter; 
... 
if(Interlocked.Increment(ref counter) == 1) 
{ 
    // yes, I'm first 
} 

線程安全的,沒有鎖。或者如果您擔心在Int32周圍打包:

if(Interlocked.CompareExchange(ref counter, 1, 0) == 0) 
{ 
    // yes, I'm first 
} 
+2

爲此+1 - 我喜歡 - 我是建議Interlocked,但不知道CompareExchange - 謝謝! – Stuart 2011-03-21 09:33:55

+0

Thanks @Marc。如果您發現時間,請查看我的編輯。你認爲'聯鎖'仍然是一個合適的解決方案嗎? – HuBeZa 2011-03-21 10:14:50

+0

@HuBeZa只要你在OnExpired中使用'Interlocked.CompareExchange(ref whatever,0,1)== 1',你應該沒問題。這表示「如果它是1,將其更改回0,並完成工作」,但是線程安全等 – 2011-03-21 10:44:57

1

我認爲最好在對象上使用lock()。

此外,還可以防止過度螺紋的鎖固使用「雙重檢查鎖定」

例如

private object _protection = new object(); 
private bool _firstTime = true; 

public bool AmIFirst() 
{ 
    if (!_firstTime) 
     return false; 

    lock (_protection) 
    { 
     if (!_firstTime) 
      return false; 

     _firstTime = false; 
     return true; 
    } 
} 

注... - 有雙重檢查鎖定了一些有趣的評論 - Double-checked locking in .NET - 我仍然在這讀了!


另一個需要注意的...它不是清楚的代碼段你貼,但如果你正在尋找實現全球單身然後http://www.yoda.arachsys.com/csharp/singleton.html溶液4開始

+0

+1雙重檢查。這不是一個單獨的,它更類似於負載平衡器。我在一些隊列中排隊這個對象,並在出列if(action.AmIFirst())action.Invoke();'。我也正在運行一個計時器到期。如果計時器在執行前進行滴答,則會調用其他一些代理。 – HuBeZa 2011-03-21 09:48:24

+0

感謝 - 我真的不能與Jon爭論 - 「就個人而言,如果我處於必須在雙重檢查鎖定和」每次鎖定「代碼之間進行選擇的情況,那麼我每次都會鎖定,直到我有確鑿證據表明它正在造成瓶頸。「從他的答案http://stackoverflow.com/questions/394898/double-checked-locking-in-net - 所以除非你知道這是一個瓶頸,那麼也許去簡單的安全單鎖? – Stuart 2011-03-21 09:52:51

0

唯一的好地方您需要確保所鎖定的同一個對象可供需要同步的所有代碼實例訪問。除此之外,沒有問題。

+0

還有第二部分需要,雖然...「,並沒有用於任何其他目的的監視器」;很難做出這樣的保證,特別是當編譯器本身用於(直到4.0)使用lock(this)來同步事件訂閱時......任何帶有事件的類,它已經在非-4.0編譯器是潛在的危險。我並不是說這肯定是一個問題 - 但只是你需要意識到這一點。 – 2011-03-21 09:26:52

2

現在,我只鎖定()我創建一個簡單的System.Object對象只是爲了鎖定。我不會鎖定()事件,而不是因爲它不會工作,但因爲我認爲這可能是相當困惑的是在一個對象上使用lock(),它本身是本身(儘管完全分開)與內核鎖定類型操作相關聯。

我不清楚你在這裏實際做了什麼,但它看起來更像是一個名爲Mutex可能會做得更好的東西。

相關問題