鎖 hashtable 應該鎖它的 SyncRoot 屬性而不應該鎖它的實例, 例如:
Hashtable ht = new Hashtable();
lock(ht.SyncRoot)
{
...
}
看了 .Net Framework 文檔, 給的例子也是鎖 SyncRoot 屬性, 說如果鎖實例的話不能保證在併發情況下的同步, 我很疑惑, 爲什麼不能鎖 hashtable 實例本身呢?
做了個實驗, 兩個線程 A 和 B, 用鎖實例和鎖 SyncRoot 兩種方式測試, 都沒有問題, 結果是一樣的。
後來, 用 Hashtable.Synchronized 創建自動線程同步的 hashtable, 終於明白了 SyncRoot 的作用。先說說自動線程同步的 Hashtable: 如果 Hashtable 要允許併發讀但只能一個線程寫, 要這麼創建 Hashtable 實例:
Hashtable hashtable = Hashtable.Synchronized(new Hashtable());
這樣, 如果有多個線程併發的企圖寫 hashtable 裏面的 item, 則同一時刻只能有一個線程寫, 其餘阻塞; 對讀的線程則不受影響。
測試的代碼是這樣的:
Hashtable _hashtable = Hashtable.Synchronized(new Hashtable());
public void TestLock()
{
Thread t1 = new Thread(new ThreadStart(SyncFunctionA));
Thread t2 = new Thread(new ThreadStart(SyncFunctionB));
t1.Start();
t2.Start();
Thread.Sleep(8000);
Console.WriteLine("hashtable[" + _key_a + "] = " + _hashtable[_key_a]);
}
private void SyncFunctionA()
{
lock (_hashtable.SyncRoot)
{
Thread.Sleep(5000);
_hashtable[_key_a] = "Value set by SyncFunctionA";
}
}
private void SyncFunctionB()
{
Console.WriteLine("hashtable[" + _key_a + "] = " + _hashtable[_key_a]);
_hashtable[_key_a] = "Value set by SyncFunctionB";
}
爲了清楚的看到效果, 線程 A 用了鎖, 並睡眠 5 秒, 睡醒後設置一下 hashtable 裏的 item. 線程 B 先讀一下 hashtable 裏的 item, 再寫 hashtable 裏的 item。因爲對 SyncRoot 加了鎖, 即使線程 B 沒有顯式的對 hashtable 加鎖, 但在 _hashtable[_key_a] = "Value set by SyncFunctionB" 一句上也會被 hashtable 自動鎖住, 直到線程 A 釋放掉 SyncRoot 鎖爲止。如果線程 A 不是鎖 SyncRoot 而是鎖 hashtable 實例本身, 那麼線程 B 不會在 _hashtable[_key_a] = "Value set by SyncFunctionB" 上被自動鎖住。
所以, 總結如下:
如果想鎖整個 hashtable, 包括讀和寫, 即不允許併發的讀和寫, 那應該鎖 hashtable 實例;
如果想允許併發的讀, 不允許併發的寫, 那應該創建 Synchronized 的 hashtable, 並對要加鎖的一塊代碼用 SyncRoot 鎖住, 如果不需要對一塊代碼加鎖, 則 hashtable 會自動對單個寫的操作加鎖。