2010-08-09 37 views
7

我正在讀一本書中的代碼清單,在果殼中的C#3,在線程中感到困惑。 在對應用服務器線程安全的話題,下面的代碼是作爲一個UserCache的例子:爲什麼在從字典中讀取時鎖定

static class UserCache 
{ 
    static Dictionary< int,User> _users = new Dictionary< int, User>(); 

    internal static User GetUser(int id) 
    { 
     User u = null; 

     lock (_users) // Why lock this??? 
      if (_users.TryGetValue(id, out u)) 
       return u; 

     u = RetrieveUser(id); //Method to retrieve from databse 

     lock (_users) _users[id] = u; //Why lock this??? 
      return u; 
    } 
} 

作者解釋了爲什麼RetrieveUser方法不是一把鎖,這是爲了避免鎖定的高速緩存更長的時間。
我很困惑,爲什麼要鎖定TryGetValue和字典的更新,因爲即使使用上面的字典正在更新兩次,如果2個線程同時調用相同的未檢索的ID。

通過鎖定字典讀取到了什麼?
非常感謝您的所有意見和見解。

回答

14

Dictionary<TKey, TValue>is not threadsafe

如果一個線程將一個鍵寫入字典,而另一個線程讀取字典,則可能會搞亂。 (例如,如果寫操作觸發數組大小調整,或者兩個鍵是散列衝突)

因此,代碼使用鎖來防止併發寫入。

+0

謝謝!這就說得通了。我想實際上沒有辦法確實地證明它。我嘗試刪除鎖並通過創建新線程在循環中調用它幾次,在更改代碼之後,每個線程總是在讀取後寫入,但它工作正常。但是與大多數線程一樣,它爲我工作的事實並不意味着它會一直工作。乾杯! – eastender 2010-08-09 17:47:22

+0

當Dictionary被單線程設置一次並且之後只能被多線程讀取時,還需要鎖定它嗎?可能不會,但另一個意見將是有用的 – Adassko 2016-05-04 08:29:12

+0

不;多次讀取都很好。 – SLaks 2016-05-04 12:56:55

1

這是一個常見的做法來訪問任何非線程安全的結構,如列表,字典,共同分享的價值觀等

和回答的主要問題:鎖定我們保證字典不會被另一個線程同時改變一個讀我們正在閱讀它的價值。這不是在字典中實現,這就是爲什麼它被稱爲非線程安全的:)

4

有當到字典中的良性競爭狀態;正如你所說的,有可能爲兩個線程確定緩存中沒有匹配的條目。在這種情況下,它們都將從數據庫中讀取數據,然後嘗試插入。只保留最後一個線程插入的對象;當第一個線程完成時,另一個對象將被垃圾收集。

讀取需要鎖定,因爲另一個線程可能同時寫入,並且讀取需要在一致的結構上進行搜索。

請注意,在.NET 4.0中引入的ConcurrentDictionary幾乎取代了這種成語。

0

如果兩個線程同時呼入並且ID存在,那麼它們都會返回正確的用戶信息。第一次鎖定是爲了防止像SLaks這樣的錯誤 - 如果有人在嘗試閱讀時正在寫字典,那麼您將遇到問題。在這種情況下,第二個鎖永遠不會到達。

如果兩個線程同時調用且id不存在,則一個線程將鎖定並輸入TryGetValue,這將返回false並將u設置爲默認值。這是第一次鎖定,以防止SLak描述的錯誤。此時,第一個線程將釋放該鎖,第二個線程將進入並執行相同操作。然後兩者都將'u'設置爲'RetrieveUser(id)'的信息;這應該是相同的信息。一個線程將鎖定字典並將_users [id]分配給u的值。第二次鎖定是因爲兩個線程正試圖同時將值寫入相同的內存位置並損壞該內存。我不知道第二個線程在進入任務時會做什麼。它將盡早返回而忽略更新,或覆蓋第一個線程中的現有數據。無論如何,詞典將包含相同的信息,因爲兩個線程都應該從RetrieveUser獲得'u'中的相同數據。

爲了提高性能,作者比較了兩種情況 - 上述情況,當兩個線程嘗試寫入相同的數據時,這種情況非常少見,並且阻塞;第二種情況下,兩個線程更有可能請求數據對於需要寫入的對象,以及存在的對象。例如,threadA和threadB同時調用,ThreadA鎖定一個不存在的id。當threadA在RetriveUser上工作時,沒有理由讓threadB等待查找。這種情況可能比上面描述的重複ID更可能,因此對於性能,作者選擇不鎖定整個塊。