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


109

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

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

Answers:


89

简短的答案:您无法轻松实现。

ConcurrentBag为每个线程保留一个线程本地队列,并且仅在其自己的队列为空时才查看其他线程的队列。如果您删除一个项目并将其放回去,那么您删除的下一个项目可能又是同一项目。无法保证反复删除项目并将其放回原处将允许您遍历所有项目。

为您提供两种选择:

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

9
SynchronizedCollection也可能是合适的替代品。
ILIA BROUDNO '16

2
@ILIABROUDNO-您应该将其作为答案!当您不需要字典时,这比冗长的ConcurrentDictionary好得多
Denis

2
仅供参考,.NET Core中不提供SynchronizedCollection。截至本评论发布之日,System.Collections.Concurrent类型是基于.NET Core的实现的最新方式。
马修·斯奈德

2
我不确定使用的是哪个版本的.NET Core,但是我正在基于.NET Core 2.1 SDK进行项目开发,现在Collections.Generic命名空间中提供了SynchronizedCollection。
卢卡斯·勒布朗

15

你不能 它是一个袋子,没有订购。当您放回原处时,只会陷入无休止的循环。

您想要一套。您可以使用ConcurrentDictionary模拟一个。或使用锁保护自己的HashSet。


8
请展开。您将使用什么作为基础ConcurrentDictionary中的键?
Denise Skidmore 2014年

2
好吧,我假设键将是您要存储的对象的类型,然后值将是某种类型的集合。HashSet正如他所描述的那样,这将“模仿” a 。
Mathias Lykkegaard Lorenzen 2014年

5

ConcurrentBag非常适合处理List,您可以在其中添加项目并从许多线程中枚举,然后按其名称的建议最终将其丢弃:)

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

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

这行得通,并且符合ConcurrentBag的设计精神。


9
我觉得这个答案有误导性。需要明确的是,这在所需的“删除”操作中不提供任何线程安全性。对其进行锁定有点违反了使用并发集合的目的。
ILIA BROUDNO

1
我同意。好吧,为了澄清一点,ConcurrentBag设计为在完成时将其全部内容填充,枚举和丢弃。任何试图删除物品的尝试(包括我的尝试)都会导致肮脏的骇客。至少我尝试提供一个答案,尽管最好的方法是使用更好的并发集合类,例如ConcurrentDictionary。
拉里

4

Mark是正确的,因为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);
    }

}

ConcurrentBag是一个无序的集合,但是您的代码希望这样做bag.TryTake并且bag.Add可以以FIFO方式工作。您的代码假设bag包括item,它循环,直到找到itembag。不鼓励仅使用代码的答案,您应该解释您的解决方案。
戴维

-1

这是我在项目中使用的扩展类。它可以从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);
        }
    }
}

很难相信它会起作用,因为您正在局部变量中创建新的ConcurrentBag,但是我不确定。有测试吗?
shtse8

3
不,它不起作用。它应该如何工作,您正在创建一个新参考。旧的仍然指向旧的对象。...如果您使用“ ref”,它会起作用
Thomas Christof

-5
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;
}

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.