C# ConcurrentBag - 添加多个项目?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10177768/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 12:45:45  来源:igfitidea点击:

ConcurrentBag - Add Multiple Items?

c#concurrencyconcurrent-collections

提问by Bob Horn

Is there a way to add multiple items to ConcurrentBag all at once, instead of one at a time? I don't see an AddRange() method on ConcurrentBag, but there is a Concat(). However, that's not working for me:

有没有办法一次向 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);
});

This code used to be in a Parallel.ForEach(), but I changed it to the above so I could troubleshoot it. The variable newList indeed has objects, but after the objectList.Concat<> line, objectList always has 0 objects in it. Does Concat<> not work that way? Do I need to add items to ConcurrentBag one at a time, with the Add() method?

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

采纳答案by Vlad

Yes :)

是的 :)

Concatis perhaps one of the Enumerableextensions. It doesn't add anything to the ConcurrentBag, it just returns some funky object containing the original bag and whatever you tried to add there.

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

Beware that the result of Concatis not a ConcurrentBaganymore, so you would not want to use it. It's a part of general LINQ framework, making possible to combine immutable sequences. This framework, of course, doesn't try to extend the concurrent properties of the operands to the result, so the resulting object will not be so well suited for multithreaded access.

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

(Basically, Concatapplies to ConcurrentBagbecause it exposes IEnumerable<T>interface.)

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

回答by Brian Gideon

Concatis an extension method provided by LINQ. It is an immutable operation that returns another IEnumerablethat can enumerate the source collection followed immediately by the specified collection. It does not, in any way, change the source collection.

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

You will need to add your items to the ConcurrentBagone at a time.

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

回答by Peter Graening

I faced a similar issue, trying to process smaller chunks of data in parallel, because one large chunk was timing out the web service I was using to access my data on the sending side but I did not want things to run slower by processing each chunk serially. Processing the data record by record was even slower - since the service I was calling could handle bulk requests, it would be better to submit as many as possible without timing out.

我遇到了类似的问题,试图并行处理较小的数据块,因为一大块使我用来在发送端访问我的数据的 Web 服务超时,但我不希望通过处理每个块来降低运行速度连续。按记录处理数据记录甚至更慢 - 由于我调用的服务可以处理批量请求,因此最好在不超时的情况下提交尽可能多的请求。

Like Vlad said, concatting a concurrent bag to a list of an object type doesn't return a concurrent bag, so concat won't work! (It took me a while to realize I couldn't do that.)

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

Try this instead - create a List<T>, and then create a ConcurrentBag<List<T>>. On each parallel iteration, it will be adding a new list to the concurrent bag. When the parallel loop is done, loop through the ConcurrentBagand concat (or union if you want to eliminate possible duplicates) to the first List<T>that you created to "flatten" everything into one list.

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

回答by Eric

(I know this is an old post, thought I'd add a little something).

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

Like others have said: yes, you need to add them one by one. In my case, I added a small extension method to make things a bit cleaner, but under the hood it does the same thing:

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

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

And then:

进而:

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

I also looked at using AsParallel to add within the extension method, but after running some tests on adding a list of strings of various sizes, it was consistantly slower to use AsParallel (as shown here) as opposed to the traditional for loop.

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

    public static void AddRange<T>(this ConcurrentBag<T> @this, IEnumerable<T> toAdd)
    {
        toAdd.AsParallel().ForAll(t => @this.Add(t));
    }

回答by Antonio Leonardo

The Concatmethod is an approach contained at public Enumerablestatic class that support System.Linqlibrary (inner .NET System.Coreassembly).

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

BUT, the Concatcontains limits to provide the "add range" requirement to ConcurrentBag<T>object, with a behavior as image bellow (at line 47 in Visual Studio):

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

enter image description here

在此处输入图片说明

To Concatmethod matches the "add range" requirement, it's necessary to renew the current ConcurrentBag<T>object instance; if the program needs to add multiples ranges, it's necessary to makes auto-reference instances from current ConcurrentBag<T>(recursively) for each range.

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

enter image description here

在此处输入图片说明

Then, I do not use Concatapproach and if I may do a recommendation, I DO NOT RECOMMEND. I follow a similar example from Eric's answer, where I developed a derived class from ConcurrentBag<T>that provides me the AddRange method using the ConcurrentBag<T>base method to add IEnumerable<T>items to the derived instance, as bellow:

然后,我不使用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);
        });
    }
}
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);
        });
    }
}