2010-06-12 321 views
74

使用.NET 4中的新增ConcurrentBag<T>,當只有TryTake()TryPeek()可用時,如何從中刪除某個特定的對象?如何從ConcurrentBag <>中刪除單個特定對象?

我想用TryTake(),然後只是增加了生成的對象回列表,如果我想刪除它,但我覺得我可能會失去了一些東西。這是正確的方法嗎?

回答

63

簡短的回答:你不能以簡單的方式做到這一點。

ConcurrentBag爲每個線程保留一個線程本地隊列,並且一旦自己的隊列變空,它只會查看其他線程的隊列。如果你刪除一個項目並放回去,那麼你刪除的下一個項目可能再次是同一個項目。不能保證反覆移除項目並放回項目將允許您迭代所有項目。你

兩種選擇:

  • 刪除所有項目,並記住他們,直到你找到你要刪除的一個,然後把其他人回來之後。請注意,如果兩個線程同時嘗試執行此操作,則會出現問題。
  • 使用更合適的數據結構,例如ConcurrentDictionary
+2

SynchronizedCollection也可能是一個合適的替代品。 – 2016-10-07 19:59:24

+0

@ILIABROUDNO - 你應該把它作爲答案!當你不需要字典 – Denis 2017-07-14 17:25:53

3

正如你所提到的,TryTake()是唯一的選擇。這也是MSDN的例子。反射器也沒有顯示其他隱藏的內部方法。

13

你不能。它的一個包,它並沒有訂購。當你把它放回去時,你會陷入無限循環。

你想要一套。你可以用ConcurrentDictionary來模擬一個。或者你用鎖來保護自己的HashSet。

+6

時,這比kludgey ConcurrentDictionary更好。請擴展。你將使用什麼作爲底層ConcurrentDictionary中的鍵? – 2014-01-10 20:19:09

+2

那麼,我認爲關鍵是你試圖存儲的對象的類型,然後這個值將是某種類型的集合。正如他所描述的,這將「模擬」一個「HashSet」。 – 2014-05-25 21:22:33

-12

怎麼樣:

bag.Where(x => x == item).Take(1); 

它的工作原理,我不知道

+0

這不會從包裏取出任何東西。您正在檢索的物品仍在包內。 – Keith 2013-12-19 19:49:07

+3

應該是「bag = new ConcurrentBag(bag.Where(x => x!= item))」 – atikot 2014-11-19 09:26:13

+3

@atikot,那條線讓我發笑 – parek 2015-06-23 09:14:59

-4
public static ConcurrentBag<String> RemoveItemFromConcurrentBag(ConcurrentBag<String> Array, String Item) 
{ 
    var Temp=new ConcurrentBag<String>(); 
    Parallel.ForEach(Array, Line => 
    { 
     if (Line != Item) Temp.Add(Line); 
    }); 
    return Temp; 
} 
1

的ConcurrentBag如何有效......是偉大的處理列表,您可以添加項目,並從枚舉許多線程,然後最終扔掉它,因爲它的名字是建議:)

As Mark Byers told,你可以重新建立一個新的並不包含你想刪除的項目,你不得不使用鎖來防止多線程命中。這是一行代碼:

myBag = new ConcurrentBag<Entry>(myBag.Except(new[] { removedEntry })); 

這個工作,並符合ConcurrentBag的設計精神。

+0

我覺得這個答案有誤導性。要清楚的是,這不會在所需的刪除操作中提供任何線程安全性。並且鎖定它有點違背了使用併發集合的目的。 – 2016-10-07 19:53:43

+0

我同意。好了,爲了澄清一下,ConcurrentBag被設計爲在完成時填充,枚舉並丟棄整個內容。任何企圖 - 包括我 - 移除物品都將導致骯髒的黑客攻擊。至少我試圖提供一個答案,但最好的是使用更好的併發集合類,如ConcurrentDictionary。 – Larry 2016-10-07 20:01:30

2

標記正確,因爲ConcurrentDictionary將以您想要的方式工作。如果你想繼續使用ConcurrentBag以下,沒有效率的介意你,會讓你在那裏。

var stringToMatch = "test"; 
var temp = new List<string>(); 
var x = new ConcurrentBag<string>(); 
for (int i = 0; i < 10; i++) 
{ 
    x.Add(string.Format("adding{0}", i)); 
} 
string y; 
while (!x.IsEmpty) 
{ 
    x.TryTake(out y); 
    if(string.Equals(y, stringToMatch, StringComparison.CurrentCultureIgnoreCase)) 
    { 
     break; 
    } 
    temp.Add(y); 
} 
foreach (var item in temp) 
{ 
    x.Add(item); 
} 
1
public static void Remove<T>(this ConcurrentBag<T> bag, T item) 
{ 
    while (bag.Count > 0) 
    { 
     T result; 
     bag.TryTake(out result); 

     if (result.Equals(item)) 
     { 
      break; 
     } 

     bag.Add(result); 
    } 

} 
2

這是我用我的項目,其中我的擴展類。它可以從一個刪除ConcurrentBag單個項目,也可以從袋中取出產品清單

public static class ConcurrentBag 
{ 
    static Object locker = new object(); 

    public static void Clear<T>(this ConcurrentBag<T> bag) 
    { 
     bag = new ConcurrentBag<T>(); 
    } 


    public static void Remove<T>(this ConcurrentBag<T> bag, List<T> itemlist) 
    { 
     try 
     { 
      lock (locker) 
      { 
       List<T> removelist = bag.ToList(); 

       Parallel.ForEach(itemlist, currentitem => { 
        removelist.Remove(currentitem); 
       }); 

       bag = new ConcurrentBag<T>(); 


       Parallel.ForEach(removelist, currentitem => 
       { 
        bag.Add(currentitem); 
       }); 
      } 

     } 
     catch (Exception ex) 
     { 
      Debug.WriteLine(ex.Message); 
     } 
    } 

    public static void Remove<T>(this ConcurrentBag<T> bag, T removeitem) 
    { 
     try 
     { 
      lock (locker) 
      { 
       List<T> removelist = bag.ToList(); 
       removelist.Remove(removeitem);     

       bag = new ConcurrentBag<T>(); 

       Parallel.ForEach(removelist, currentitem => 
       { 
        bag.Add(currentitem); 
       }); 
      } 

     } 
     catch (Exception ex) 
     { 
      Debug.WriteLine(ex.Message); 
     } 
    } 
} 
相關問題