2010-06-12 284 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); 
     } 
    } 
} 
相关问题