如何从ConcurrentBag <>中删除单个特定对象?

Bra*_*don 102 c# c#-4.0

随着新ConcurrentBag<T>的.NET 4,你怎么从它,只有当删除一些具体目标TryTake()TryPeek()可用?

我想用的TryTake(),然后只是增加了生成的对象回列表,如果我想要删除它,但我觉得我可能会失去了一些东西.这是正确的方法吗?

Mar*_*ers 83

简短的回答:你不能轻易做到这一点.

ConcurrentBag为每个线程保留一个线程本地队列,并且只有在其自己的队列变空时才查看其他线程的队列.如果您删除了一个项目并将其放回原来,那么您删除的下一个项目可能会再次成为同一个项目.无法保证重复删除项目并将其放回将允许您迭代所有项目.

两种替代方案:

  • 删除所有项目并记住它们,直到找到要删除的项目,然后将其他项目放回原处.请注意,如果两个线程同时尝试执行此操作,则会出现问题.
  • 使用更合适的数据结构,例如ConcurrentDictionary.

  • SynchronizedCollection也可能是一个合适的替代品. (7认同)
  • @ILIABROUDNO——你应该把它作为答案!当您不需要字典时,这比笨拙的 ConcurrentDictionary 好得多 (2认同)
  • 仅供参考,SynchronizedCollection 在 .NET Core 中不可用。截至本评论发布之日,System.Collections.Concurrent 类型是基于 .NET Core 的实现的当前方式。 (2认同)
  • 我不确定正在使用哪个版本的 .NET Core,但我正在开发一个基于 .NET Core 2.1 SDK 的项目,并且 SynchronizedCollection 现在可在 Collections.Generic 命名空间中使用。 (2认同)

Han*_*ant 15

你不能.它是一个袋子,它没有订购.当你把它放回去时,你将陷入无休止的循环中.

你想要一套.您可以使用ConcurrentDictionary模拟一个.或者是一个用锁来保护自己的HashSet.

  • 请扩大.您将使用什么作为底层ConcurrentDictionary中的键? (7认同)
  • 好吧,我假设密钥将是您尝试存储的对象的类型,然后该值将是某些类型的集合.那会像他描述的那样"模仿"一个"HashSet". (2认同)

Lar*_*rry 7

ConcurrentBag 非常适合处理一个列表,您可以在其中添加项目并从多个线程中枚举,然后最终将其扔掉,正如它的名字所暗示的那样:)

正如 Mark Byers 所说,您可以重新构建一个新的 ConcurrentBag,其中不包含您要删除的项目,但您必须使用锁来保护它免受多线程命中。这是一个单行:

myBag = new ConcurrentBag<Entry>(myBag.Except(new[] { removedEntry }));
Run Code Online (Sandbox Code Playgroud)

这是有效的,并且符合 ConcurrentBag 的设计精神。

  • 我觉得这个答案有误导性。需要明确的是,这不会在所需的 Remove 操作中提供任何线程安全性。并且在它周围加锁有点违背使用并发集合的目的。 (10认同)

小智 5

马克是正确的,因为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);
}
Run Code Online (Sandbox Code Playgroud)