2016-11-17 28 views
3

我不知道這是否是線程安全的或(如果不是),我怎麼可以使之安全。它從一個計時器叫:當我在兩個連續的語句中讀/寫它時,是否需要鎖定一個布爾值?

private volatile bool _isSynchronizing; 

private void SynchronizeSessionCache(object state = null) 
{ 
    if (_isSynchronizing) 
    { 
     Log.Warn($"Aborted synchronization of SessionCache with SessionManager because we are already synchronizing. Interval is: {SynchronizationInterval}"); 
     return; 
    } 

    _isSynchronizing = true; 

    bool lockWasTaken = false; 
    try 
    { 
     // some code that doesn't need a lock ... 
     // ... 

     // lock this part 
     Monitor.Enter(_lockObject, ref lockWasTaken); 
     // main code ... 
    } 
    finally // omitted catch 
    { 
     _isSynchronizing = false; 
     if(lockWasTaken) 
      Monitor.Exit(_lockObject); 
    } 
} 

我擔心的是,它可能是可能的,在他的方法開始一個線程檢查_isSynchronizing和它的false在這個時候。然後另一個線程進入主體,因爲線程尚未將其設置爲true。即使_isSynchronizingvolatile,這可能嗎?如果是這樣,使這個方法線程安全的最好方法是什麼?

如果我的理解正確volatile不能阻止這樣的競爭條件,但只能確保變量沒有被緩存,所以所有線程總是讀取當前值。

+0

是有可能爲兩個胎面越過第一檢查。但是在內部方法中你有一個鎖,所以你期望多個線程進入,所以有什麼想法? – Evk

+0

@Evk:我不希望多個線程進入。 '_lockObject'也用於其他需要訪問共享資源的方法和事件。這在同步過程中必須鎖定。它不應該發生多個線程同時進入。這就是爲什麼我使用_isSynchronizing標誌。 –

+0

那麼我會在這裏使用雙重檢查鎖定。 – Evk

回答

4

爲了保證線程安全,你必須做出比較,並分配給單個原子操作。否則,在第一個線程執行任務時,另一個線程總有可能通過比較。該volatile關鍵字不幫助這裏,因爲它相當告訴任何優化允許編譯器(和運行時),這可能會改變這個變量參與到讀寫的操作順序。

(更多關於volatile的事情,你可以在this大文章埃裏克利珀閱讀。)

簡單(略慢一個)的解決辦法是在各地設立的比較和分配的關鍵部分:

lock (_someLockObject) 
{ 
    if (_isSynchronizing) 
    { 
     return; 
    } 

    _isSynchronizing = true;  
} 

然而有一個更快的解決方案,而不是爲bool變量。對於int,您可以使用Interlocked.CompareExchange()方法。

讓我們假設int _isSynchronizing = 0意味着false_isSynchronizing = 1意味着true

然後你可以使用以下語句:

if (Interlocked.CompareExchange(ref _isSynchronizing, 1, 0) == 1) 
{ 
    // If the original val == 1, we're already synchronizing 
    return; 
} 

這比使用Monitor稍快,但沒有bool超載。

+0

爲了清晰起見,在這種情況下。使用'''Interlocked.Decrement(ref _isSynchronizing);'''或者直接賦值爲零而不用鎖等是正確的 – NevilleDastur

相關問題