2010-10-27 29 views
3

昨天我問了一個關於這個問題的問題,並得到了很多有用的反饋(謝謝!),但我認爲我沒有給出足夠的信息 - 因此,另一個問題。無法安全鎖定ConcurrentDictionary的值

我有兩個線程同時讀取兩個文件。他們將來自這些文件的信息放入兩個ConcurrentQueue中。另外兩個線程隨之出現,從ConcurrentQueues中取出項並將項放入單個ConcurrentDictionary中。更新字典中的項目時,線程可能必須創建一個新對象,或者只是通知當前對象有更多信息進來。在後一種情況下,有時會發生漫長的掃描。有時候,在這次掃描之後,對象說它可以將其刪除(作爲一種節省內存的嘗試),並且該線程將其從字典中移除。

我現在的(碎)下面的代碼:

string dictionaryKey = myMessage.someValue; 

Monitor.Enter(GetDictionaryLocker); 
DictionaryObject currentObject = myConcurrentDictionary.GetOrAdd(dictionaryKey, new DictionaryObject()); 
// we can be interrupted here 
lock (currentObject) 
{ 
    Monitor.Exit(GetDictionaryLocker); 
    //KeyNotFoundException is possible on line below 
    if (myConcurrentDictionary[dictonaryKey].scan(myMessage)) // Scans the message - returns true if the object says its OK to remove it from the dictionary 
    { 
     DictionaryObject temp;      // It's OK to delete it 
     if (!queuedMessages.TryRemove(ric, out temp)) // Did delete work? 
      throw new Exception("Was unable to delete a DictionaryObject that just reported it was ok to delete it"); 
    } 
} 

什麼情況是這樣的:

由於發現我想在字典中的對象之間:

DictionaryObject currentObject = myConcurrentDictionary.GetOrAdd(dictionaryKey, new DictionaryObject()); 

,然後鎖定在該對象:

lock (currentObject) 

,線程可以interuppted,所以那裏有一個機會,另一個線程的時候我避開試圖在這裏訪問它刪除的對象了字典:

if (myConcurrentDictionary[dictonaryKey].scan(myMessage)) 

這就導致KeyNotFoundException。我需要一些自動鎖定對象的方法。

正如我所說的,我昨天得到了一些建議,但我不明白他們

  • 其中一張海報提到,我應該嘗試首先從字典中刪除項目,因爲這是一個我可以如何使用使用ConcurrentDictionary進行原子操作,然後重新添加它們。然而,我不確定我會如何向其他線程表明它應該等待該項目被重新添加,而不是僅僅考慮它的缺失值並創建它。
  • 另一個海報帶來了Threading.Interlocked.CompareExchange,我可以用它來標記該對象正在使用中。但我不知道如何處理正在使用的對象的情況 - 我將如何等待?

我有一些限制:我必須按順序處理ConcurrentQueues,所以我不能放棄將對象放在字典中,或稍後再回來 - 我需要阻止。該詞典可能包含500,000個或更多項目,所以我確實需要ConcurrentDictionary的O(1)查找時間。

任何想法?很抱歉的長期職位

感謝,

弗雷德裏克

+0

更新現有的文章而不是創建新的文章會更好嗎?這樣,所有有用的提示將會失去幫助你的新人。 – 2010-10-27 10:34:04

+0

對不起,大衛,這個網站的新手 - 在另一個線程的海報要求我發佈一個新的線程與額外的信息。鏈接在這裏:http://stackoverflow.com/questions/4025428/cant-safely-lock-a-value-of-a-concurrentdictionary – Frederik 2010-10-27 10:36:36

回答

1

你可以改變掃描線成這樣:

DictionaryItemType dictionaryItem; 

if (myConcurrentDictionary.TryGetValue(dictonaryKey, out dictionaryItem)) 
{ 
    if (dictionaryItem.scan(myMessage)) 

這樣你重新檢查該項目是否仍然在字典中,如果它不是,只是不進入掃描分支。

+0

嗨彼得。不幸的是,我不認爲這是線程安全的。兩個線程可以同時輸入if語句 - 第一個會調用delete,然後第二個線程不能訪問它認爲存在的字典中的項 – Frederik 2010-10-27 10:46:27

+0

TryGetValue也是一個原子操作,它意味着在如果你確實有'dictionaryItem'的引用。有沒有我沒有看到的問題? – 2010-10-27 11:01:21

+0

當然,我有一個引用,但是如果dictionaryItem不再處於集合中,我只會更新一個不再可訪問的引用。換句話說,如果我正在更新某些東西,則不應允許其他人同時從集合中刪除它 – Frederik 2010-10-27 11:29:44