web-dev-qa-db-ja.com

ConcurrentBag-複数のアイテムを追加しますか?

一度に1つではなく、一度に複数の項目を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には常にオブジェクトがありません。 Concat <>はそのように機能しませんか? Add()メソッドを使用して、アイテムを一度に1つずつConcurrentBagに追加する必要がありますか?

36
Bob Horn

はい :)

Concatは、おそらくEnumerable拡張機能の1つです。 ConcurrentBagには何も追加せず、元のバッグとそこに追加しようとしたものを含むファンキーなオブジェクトを返すだけです。

Concatの結果がConcurrentBagではなくなったことに注意してください。そのため、これを使用する必要はありません。これは一般的なLINQフレームワークの一部であり、不変のシーケンスを組み合わせることができます。もちろん、このフレームワークは、オペランドの並行プロパティを結果に拡張しようとしないため、結果のオブジェクトはマルチスレッドアクセスにあまり適していません。

(基本的に、ConcatIEnumerable<T>インターフェースを公開するため、ConcurrentBagに適用されます。)

2
Vlad

(私はこれが古い記事であることを知っています、少し何かを追加すると思いました)。

他の人が言ったように:はい、あなたはそれらを一つずつ追加する必要があります。私の場合、少し拡張するために小さな拡張メソッドを追加しましたが、内部的には同じことを行います。

    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));
    }
41
Eric

Concatは、LINQが提供する拡張メソッドです。これは不変の操作であり、別のIEnumerableを返します。このコレクションは、ソースコレクションを列挙し、その直後に指定されたコレクションを列挙できます。ソースコレクションは変更されません。

ConcurrentBagに一度に1つずつアイテムを追加する必要があります。

20
Brian Gideon

1つの大きなチャンクが送信側のデータへのアクセスに使用していたWebサービスをタイムアウトしていたため、同様の問題に直面し、データの小さなチャンクを並行して処理しようとしましたが、各チャンクを処理することで物事を遅く実行したくありませんでしたシリアル。レコードごとのデータ処理はさらに遅くなりました-私が呼び出していたサービスは大量のリクエストを処理できるため、タイムアウトせずにできるだけ多く送信することをお勧めします。

Vladが言ったように、オブジェクトタイプのリストにコンカレントバッグを連結してもコンカレントバッグは返されないため、concatは機能しません。 (それができないことに気づくのにしばらく時間がかかりました。)

代わりにこれを試してください-List<T>を作成してから、ConcurrentBag<List<T>>を作成します。並列反復ごとに、並行バッグに新しいリストが追加されます。並列ループが完了したら、ConcurrentBagをループし、すべてを1つのリストに「平坦化」するために作成した最初のList<T>に連結(または、重複を排除したい場合は共用体)します。

6
Peter Graening

ConcatメソッドはpublicEnumerableに含まれるアプローチで、System.Linqライブラリ(内部.NET System.Coreアセンブリ)。

しかし、Concatには ConcurrentBag<T> オブジェクトに「範囲の追加」要件を提供するための制限が含まれており、次のように動作します(Visual Studioの47行目):

enter image description here

Concatメソッドが「範囲の追加」要件に一致するには、現在の ConcurrentBag<T> オブジェクトインスタンスを更新する必要があります。プログラムで複数の範囲を追加する必要がある場合は、各範囲に対して現在の ConcurrentBag<T> (再帰的に)から自動参照インスタンスを作成する必要があります。

enter image description here

それから、私はConcatアプローチを使用せず、推奨を行う可能性がある場合は、推奨しません。私は Eric の回答から同様の例に従いますが、ここでは ConcurrentBag<T>ConcurrentBag<T> 基本メソッドを使用してAddRangeメソッドを提供し、次のように派生インスタンスに 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);
        });
    }
}
0