【发布时间】:2011-03-03 01:41:20
【问题描述】:
使用 .NET 4 中的新 ConcurrentBag<T>,当只有 TryTake() 和 TryPeek() 可用时,如何从中删除某个特定对象?
我正在考虑使用TryTake(),然后如果我不想想要将其删除,则只需将结果对象添加回列表中,但我觉得我可能会遗漏一些东西。这是正确的方法吗?
【问题讨论】:
使用 .NET 4 中的新 ConcurrentBag<T>,当只有 TryTake() 和 TryPeek() 可用时,如何从中删除某个特定对象?
我正在考虑使用TryTake(),然后如果我不想想要将其删除,则只需将结果对象添加回列表中,但我觉得我可能会遗漏一些东西。这是正确的方法吗?
【问题讨论】:
这是我在项目中使用的扩展类。它可以从 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);
}
}
}
【讨论】:
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 以先进先出的方式工作。您的代码假定bag 包含item,它会循环直到在bag 中找到item。不鼓励仅使用代码的答案,您应该解释您的解决方案。
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);
}
【讨论】:
ConcurrentBag 非常适合处理 List,您可以在其中添加项目并从多个线程枚举,然后最终将其丢弃,正如其名称所暗示的那样:)
As Mark Byers told,您可以重新构建一个新的 ConcurrentBag,其中不包含您要删除的项目,但您必须使用锁来保护它免受多个线程的影响。这是一个单行:
myBag = new ConcurrentBag<Entry>(myBag.Except(new[] { removedEntry }));
这很有效,并且符合 ConcurrentBag 的设计精神。
【讨论】:
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;
}
【讨论】:
怎么样:
bag.Where(x => x == item).Take(1);
它有效,我不确定效率如何......
【讨论】:
你不能。它是一个袋子,它没有被订购。当你把它放回去时,你只会陷入无限循环。
你想要一套。您可以使用 ConcurrentDictionary 模拟一个。或者一个用锁保护自己的 HashSet。
【讨论】:
HashSet。
正如您提到的,TryTake() 是唯一的选择。这也是MSDN 上的示例。 Reflector 也没有显示其他隐藏的感兴趣的内部方法。
【讨论】:
简短的回答:你不能简单地做到这一点。
ConcurrentBag 为每个线程保留一个线程本地队列,并且只有在它自己的队列为空时才会查看其他线程的队列。如果您移除一个项目并将其放回原处,那么您移除的下一个项目可能又是同一个项目。不能保证反复删除项目并将它们放回原处将允许您迭代所有项目。
两种选择:
【讨论】: