49

有没有办法一次将多个项目添加到 ConcurrentBag,而不是一次一个?我在 ConcurrentBag 上没有看到 AddRange() 方法,但有一个 Concat()。但是,这对我不起作用:

ConcurrentBag<T> objectList = new ConcurrentBag<T>();

timeChunks.ForEach(timeChunk =>
{
    List<T> newList = Foo.SomeMethod<T>(x => x.SomeReadTime > timeChunk.StartTime);
    objectList.Concat<T>(newList);
});

此代码曾经位于 Parallel.ForEach() 中,但我将其更改为上述代码,以便对其进行故障排除。变量 newList 确实有对象,但是在 objectList.Concat<> 行之后,objectList 中总是有 0 个对象。Concat<> 不是这样工作的吗?我是否需要使用 Add() 方法一次向 ConcurrentBag 添加一个项目?

4

6 回答 6

73

(我知道这是一个旧帖子,我想我会添加一些东西)。

就像其他人说的:是的,您需要一一添加。在我的例子中,我添加了一个小的扩展方法来让事情变得更简洁,但在引擎盖下它做同样的事情:

    public static void AddRange<T>(this ConcurrentBag<T> @this, IEnumerable<T> toAdd)
    {
        foreach (var element in toAdd)
        {
            @this.Add(element);
        }
    }

进而:

    ConcurrentBag<int> ccBag = new ConcurrentBag<int>();
    var listOfThings = new List<int>() { 1, 2, 4, 5, 6, 7, 8, 9 };
    ccBag.AddRange(listOfThings);

我还研究了使用 AsParallel 在扩展方法中添加,但是在对添加各种大小的字符串列表进行了一些测试之后,与传统的 for 循环相比,使用 AsParallel(如此处所示)始终慢。

    public static void AddRange<T>(this ConcurrentBag<T> @this, IEnumerable<T> toAdd)
    {
        toAdd.AsParallel().ForAll(t => @this.Add(t));
    }
于 2015-06-30T17:38:04.550 回答
20

Concat是LINQ提供的一种扩展方法。它是一个不可变的操作,它返回另一个IEnumerable可以枚举源集合,紧随其后的是指定集合。它不会以任何方式更改源集合。

您将需要一次将您的项目添加到ConcurrentBag其中一个。

于 2012-04-16T16:18:48.160 回答
9

我遇到了类似的问题,试图并行处理较小的数据块,因为一个大块正在超时我用来在发送端访问我的数据的 Web 服务,但我不希望通过处理每个块来让事情运行得更慢串行。逐条处理数据记录甚至更慢 - 因为我调用的服务可以处理批量请求,所以最好尽可能多地提交而不超时。

就像弗拉德说的那样,将并发包连接到对象类型的列表不会返回并发包,所以 concat 不起作用!(我花了一段时间才意识到我做不到。)

试试这个 - 创建一个List<T>,然后创建一个ConcurrentBag<List<T>>. 在每次并行迭代中,它将向并发包中添加一个新列表。并行循环完成后,循环遍历ConcurrentBagand concat(或联合,如果您想消除可能的重复项)到List<T>您创建的第一个,以将所有内容“展平”到一个列表中。

于 2015-05-21T14:09:50.273 回答
2

Concat方法是包含在支持库(内部程序集)的公共Enumerable静态类中的一种方法。System.Linq.NET System.Core

但是,Concat包含限制以向ConcurrentBag<T>对象提供“添加范围”要求,其行为如下图所示(在 Visual Studio 中的第 47 行):

在此处输入图像描述

Concat方法匹配“添加范围”要求,需要更新当前ConcurrentBag<T>对象实例;如果程序需要添加多个范围,则有必要从当前ConcurrentBag<T>(递归)为每个范围创建自动引用实例。

在此处输入图像描述

然后,我不使用Concat方法,如果我可以推荐,我不推荐。我遵循Eric的回答中的一个类似示例,在那里我开发了一个派生类,从中ConcurrentBag<T>为我提供了 AddRange 方法,该方法使用ConcurrentBag<T>基本方法将IEnumerable<T>项目添加到派生实例,如下所示:

public class ConcurrentBagCompleted<T> : ConcurrentBag<T>
{
    public ConcurrentBagCompleted() : base() { }

    public ConcurrentBagCompleted(IEnumerable<T> collection):base(collection)
    {
    }

    public void AddRange(IEnumerable<T> collection)
    {
        Parallel.ForEach(collection, item =>
        {
            base.Add(item);
        });
    }
}
于 2020-01-29T16:39:33.093 回答
0

还有第二个构造函数,ConcurrentBag<T>它以 IEnumerable 作为参数并从中构造包。对于这种情况,这是我发现的最快、最安全的方法。请参考ConcurrentBag<T>(IEnumerable<T>) 所以,在问题的情况下,解决方案是:

            ConcurrentBag<T> objectList = new ConcurrentBag<T>();

        timeChunks.ForEach(timeChunk =>
        {
            List<T> newList = Foo.SomeMethod<T>(x => x.SomeReadTime > timeChunk.StartTime);
            newList.AddRange(objectList.ToList());
            objectList=new ConcurrentBag<T>(newList);
        });
于 2021-09-24T13:21:27.817 回答
-5

是的 :)

Concat也许是Enumerable扩展之一。它不会向 中添加任何内容ConcurrentBag,它只是返回一些时髦的对象,其中包含原始包以及您尝试添加的任何内容。

请注意,结果不再Concat是 a ConcurrentBag,因此您不想使用它。它是通用 LINQ 框架的一部分,可以组合不可变序列。当然,该框架不会尝试将操作数的并发属性扩展到结果,因此结果对象将不太适合多线程访问。

(基本上,Concat适用于ConcurrentBag因为它暴露了IEnumerable<T>接口。)

于 2012-04-16T16:16:03.953 回答