2011-11-23 106 views
5

我試圖使用ReaderWriterLockSlim類來管理列表。ReaderWriterLockSlim阻塞讀取,直到所有排隊的寫入完成

對這個列表有許多讀取和很少寫入,我的讀取速度很快,而我的寫入很慢。

我有一個簡單的測試工具,用來檢查鎖的工作方式。

如果發生以下情況

Thread 1 - Start Write 
Thread 2 - Start Read 
Thread 3 - Start Write 

那麼結果如下

Thread 1 starts its write and locks the list. 
Thread 2 adds itself to the read queue. 
Thread 3 adds itself to the write queue. 
Thread 1 finishes writing and releases the lock 
Thread 3 aquires the lock and starts its write 
Thread 3 finishes writing and releases the lock 
Thread 2 performs its read 

是否有換鎖的行爲的任何方式,使得被前排隊的任何讀請求寫鎖定被允許在寫鎖定被授予之前完成?

編輯:演示我的問題是下面

public partial class SimpleLock : System.Web.UI.Page 
{ 
    public static ReaderWriterLockSlim threadLock = new ReaderWriterLockSlim(); 

    protected void Page_Load(object sender, EventArgs e) 
    { 
     List<String> outputList = new List<String>(); 

     Thread thread1 = new Thread(
      delegate(object output) 
      { 
       ((List<String>)output).Add("Write 1 Enter"); 
       threadLock.EnterWriteLock(); 
       ((List<String>)output).Add("Write 1 Begin"); 
       Thread.Sleep(100); 
       ((List<String>)output).Add("Write 1 End"); 
       threadLock.ExitWriteLock(); 
       ((List<String>)output).Add("Write 1 Exit"); 
      } 
     ); 
     thread1.Start(outputList); 

     Thread.Sleep(10); 

     Thread thread2 = new Thread(
      delegate(object output) 
      { 
       ((List<String>)output).Add("Read 2 Enter"); 
       threadLock.EnterReadLock(); 
       ((List<String>)output).Add("Read 2 Begin"); 
       Thread.Sleep(100); 
       ((List<String>)output).Add("Read 2 End"); 
       threadLock.ExitReadLock(); 
       ((List<String>)output).Add("Read 2 Exit"); 
      } 
     ); 
     thread2.Start(outputList); 

     Thread.Sleep(10); 

     Thread thread3 = new Thread(
      delegate(object output) 
      { 
       ((List<String>)output).Add("Write 3 Enter"); 
       threadLock.EnterWriteLock(); 
       ((List<String>)output).Add("Write 3 Begin"); 
       Thread.Sleep(100); 
       ((List<String>)output).Add("Write 3 End"); 
       threadLock.ExitWriteLock(); 
       ((List<String>)output).Add("Write 3 Exit"); 
      } 
     ); 
     thread3.Start(outputList); 

     thread1.Join(); 
     thread2.Join(); 
     thread3.Join(); 

     Response.Write(String.Join("<br />", outputList.ToArray())); 
    } 
} 
+0

直接回答你的問題,「有沒有改變鎖的行爲的方法」,答案是否定的。 [ReaderWriterLockSlim]的文檔(http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim.aspx)說,「一個線程,如果有線程等待輸入寫入,則嘗試進入讀取模式塊模式下,或者在寫入模式下是否有單個線程。「目前還沒有辦法改變這種行爲。 –

回答

3

的代碼是否有換鎖的行爲的任何方式,使得被寫入鎖之前排隊任何 讀請求允許在授予寫鎖之前完成 ?

那麼幾乎完全避免使用鎖呢?在寫入過程中,您可以獲取鎖定,複製原始數據結構,修改副本,然後通過將舊引用換爲新引用來發布新數據結構。由於您在數據結構「發佈」之後從不修改數據結構,因此您根本不需要鎖定讀取。

這裏是它如何工作的:

public class Example 
{ 
    private object writelock = new object(); 
    private volatile List<string> data = new List<string>(); 

    public void Write(string item) 
    { 
    lock (writelock) 
    { 
     var copy = new List<string>(data); // Create the copy. 
     copy.Add(item); // Modify the data structure. 
     data = copy; // Publish the modified data structure. 
    } 
    } 

    public string Read(int index) 
    { 
    return data[index]; 
    } 
} 

我們正在利用這裏的技巧是什麼的是由data變量引用的不變性。我們唯一需要做的是將變量標記爲volatile

請注意,此技巧僅適用於寫入足夠少且數據結構足夠小以保持複製操作便宜的情況。不是所有的解決方案都是。它並不適合所有情況,但它可能只適用於您。

+0

這是我們去過的方法。它實際上有一個巨大的開銷,因爲「列表」的大小很大。複製需要很長時間。然而,目前似乎還在持續。 –

+1

爲了補充說明,在您的示例中,在寫入期間,您將創建列表的第三個版本,然後將其替換爲快照。實際上,您可以維護所有讀取都完成的「主」列表。然後Write可以「鎖定」主設備,這樣就不會發生進一步的寫操作。它的第一步是製作主人的副本。然後根據需要更新副本,然後用副本替換「主」。 –

+0

@RobinDay:是的,非常棒!我沒有想過這個。這相當大地減少了內存消耗。爲了反映你的想法,我做出了一些重大改變。 –

相關問題