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

时间:2010-06-12 19:16:51

标签: c# c#-4.0

使用.NET 4中的新ConcurrentBag<T>,当只有TryTake()TryPeek()可用时,如何从中删除某个特定对象?

我正在考虑使用TryTake()然后只是将结果对象添加回列表中,如果我想要删除它,但我觉得我可能会丢失一些东西。这是正确的方法吗?

10 个答案:

答案 0 :(得分:77)

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

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

两种替代方案:

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

答案 1 :(得分:15)

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

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

答案 2 :(得分:4)

ConcurrentBag非常适合处理List,你可以在其中添加项目并从多个线程枚举,然后最终将其丢弃,因为它的名字暗示:)

As Mark Byers told,您可以重新构建一个不包含您要删除的项目的新ConcurrentBag,但是您必须使用锁来防止多个线程命中。这是一个单行:

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

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

答案 3 :(得分:3)

正如您所提到的,TryTake()是唯一的选择。这也是MSDN上的示例。反射器也没有显示其他隐藏的内部感兴趣的方法。

答案 4 :(得分:3)

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

答案 5 :(得分: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);
    }

}

答案 6 :(得分:0)

改为尝试BlockingCollection<T>。在.Net Core中可用。

答案 7 :(得分:-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);
        }
    }
}

答案 8 :(得分:-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;
}

答案 9 :(得分:-13)

怎么样:

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

它有效,我不确定效率如何......