2008-10-21 56 views
4

我試圖阻止多線程服務器中的數據競爭。我的問題如下:有一個List<RServer>,類型RServer是一個有幾個字段的類。現在,服務器有幾個線程同時運行,他們可以修改List(添加更多項目)和個人RServer實例(更改字段)。c#鎖定

所以我的策略是讓每個RServer情況下,另外一個readonly object RServerListLock = new object()readonly object RServerLock = new object()並在lock附上所有的修改或者(在ListRServer實例)的代碼。這安全嗎?如果一個線程試圖鎖定一個RServerLock另一個鎖定它會發生什麼?

回答

8

如果你有一個競爭鎖,第二個線程必須等到第一個釋放鎖。

你的計劃聽起來好 - 但你需要的時候數據以及鎖定,以確保您獲得最新的值,且一致的。否則,你可能會在一個線程中寫入一些值的一半,並在一個不同的線程中同時看到一些新的值 - 但可能不是全部 - 和舊值。

如果你可以儘量避免這樣做,你的生活會更容易:)不可變的類型使線程更簡單。

不要忘記,如果你有代碼需要兩個鎖在同一時間(例如,添加一個RServer並修改另一個,原子地)必須確保您總是以相同的順序獲取鎖 - 如果一個線程試圖獲取鎖B,同時它持有鎖A,而另一個線程試圖獲取鎖A拿着鎖B,你會以僵局結束。

查看我的threading tutorialJoe Albahari's瞭解更多詳情。另外,如果你對併發感興趣,Joe Duffy有一個excellent book,它即將推出。

0

這是安全的。如果一個線程獲取了鎖,其他線程將不得不等待,直到鎖被釋放。

但是,雖然不太可能,但您可能會遇到性能問題,因爲鎖定可能會超過全局。這真的取決於你的狀態是什麼以及它是如何被這些線程改變的,所以我不能幫你做到這一點。

0

重申一點 - 每個RServer實例都有一個名爲RServerLock的變量,它將使用鎖定塊進行鎖定。

線程1(T1)在RServer 1(R1)上鎖定。線程2(T2)嘗試修改R1,導致R1鎖定塊被輸入。在這種情況下,T2將等到T1完成。

有一件事要小心的是最終有多少個RServer實例。如果最終導致實例數量過多,那麼您只是爲了鎖定而攜帶額外的20個字節的數據。此時,您可能需要考慮鎖定條帶。

2

如果一個線程試圖鎖定一個已經被鎖定的對象,它將會阻塞直到鎖定被釋放。當兩個線程試圖同時鎖定它時,並不存在併發問題,因爲鎖是原子操作,並且其中一個線程將始終成爲鎖的受害者並最終阻塞。

只要您需要RServer實例的完整鎖定,您的策略聽起來很合理。如果您可以專門鎖定特定的RServer實例字段,那可能會更有效。然而,它會增加鎖定操作的數量並且會更加複雜。

4

看起來你有一個ReaderWriterLock的主要候選人。最好的類(如果你的運行時支持它,我認爲3.0+)是ReaderWriterLockSlim,因爲原來的ReaderWriterLock有性能問題。

其中一位MSDN雜誌的作者也遇到了RWLS類的問題,我不會在這裏詳細討論,但你可以看看它here

我知道下面的代碼會產生IDisposable純粹主義者的憤怒,但有時它確實會產生很好的語法糖。在任何情況下,你可能會發現以下有用:

/// <summary> 
    /// Opens the specified reader writer lock in read mode, 
    /// specifying whether or not it may be upgraded. 
    /// </summary> 
    /// <param name="slim"></param> 
    /// <param name="upgradeable"></param> 
    /// <returns></returns> 
    public static IDisposable Read(this ReaderWriterLockSlim slim, bool upgradeable) 
    { 
     return new ReaderWriterLockSlimController(slim, true, upgradeable); 
    } // IDisposable Read 

    /// <summary> 
    /// Opens the specified reader writer lock in read mode, 
    /// and does not allow upgrading. 
    /// </summary> 
    /// <param name="slim"></param> 
    /// <returns></returns> 
    public static IDisposable Read(this ReaderWriterLockSlim slim) 
    { 
     return new ReaderWriterLockSlimController(slim, true, false); 
    } // IDisposable Read 

    /// <summary> 
    /// Opens the specified reader writer lock in write mode. 
    /// </summary> 
    /// <param name="slim"></param> 
    /// <returns></returns> 
    public static IDisposable Write(this ReaderWriterLockSlim slim) 
    { 
     return new ReaderWriterLockSlimController(slim, false, false); 
    } // IDisposable Write 

    private class ReaderWriterLockSlimController : IDisposable 
    { 
     #region Fields 

     private bool _closed = false; 
     private bool _read = false; 
     private ReaderWriterLockSlim _slim; 
     private bool _upgrade = false; 

     #endregion Fields 

     #region Constructors 

     public ReaderWriterLockSlimController(ReaderWriterLockSlim slim, bool read, bool upgrade) 
     { 
      _slim = slim; 
      _read = read; 
      _upgrade = upgrade; 

      if (_read) 
      { 
       if (upgrade) 
       { 
        _slim.EnterUpgradeableReadLock(); 
       } 
       else 
       { 
        _slim.EnterReadLock(); 
       } 
      } 
      else 
      { 
       _slim.EnterWriteLock(); 
      } 
     } // ReaderWriterLockSlimController 

     ~ReaderWriterLockSlimController() 
     { 
      Dispose(); 
     } // ~ReaderWriterLockSlimController 

     #endregion Constructors 

     #region Methods 

     public void Dispose() 
     { 
      if (_closed) 
       return; 
      _closed = true; 

      if (_read) 
      { 
       if (_upgrade) 
       { 
        _slim.ExitUpgradeableReadLock(); 
       } 
       else 
       { 
        _slim.ExitReadLock(); 
       } 
      } 
      else 
      { 
       _slim.ExitWriteLock(); 
      } 

      GC.SuppressFinalize(this); 
     } // void Dispose 

     #endregion Methods 
    } // Class ReaderWriterLockSlimController 

假如把它放在一個擴展方法類(公共靜態類[名])和按如下方式使用它:

using(myReaderWriterLockSlim.Read()) 
{ 
    // Do read operations. 
} 

或者

using(myReaderWriterLockSlim.Read(true)) 
{ 
    // Read a flag. 
    if(flag) 
    { 
    using(myReaderWriterLockSlim.Write()) // Because we said Read(true). 
    { 
     // Do read/write operations. 
    } 
    } 
} 

using(myReaderWriterLockSlim.Write()) // This means you can also safely read. 
{ 
    // Do read/write operations. 
}