7

foreachC#で並列列挙型に対してスタイルの反復を行う方法はありますか? for添え字付きリストの場合、インデックス範囲で int を反復する通常のループを使用できることはわかっていますが、いくつかの理由でそれを好むforeachのです。for

C# 2.0 で動作する場合のボーナス ポイント

4

6 に答える 6

11

.NET 4 の BlockingCollection を使用すると、これが非常に簡単になります。BlockingCollection を作成し、その .GetConsumingEnumerable() を列挙可能なメソッドで返します。次に、 foreach は単純にブロッキング コレクションに追加します。

例えば

private BlockingCollection<T> m_data = new BlockingCollection<T>();

public IEnumerable<T> GetData( IEnumerable<IEnumerable<T>> sources )
{
    Task.Factory.StartNew( () => ParallelGetData( sources ) );
    return m_data.GetConsumingEnumerable();
}

private void ParallelGetData( IEnumerable<IEnumerable<T>> sources )
{
    foreach( var source in sources )
    {
        foreach( var item in source )
        {
            m_data.Add( item );
        };
    }

    //Adding complete, the enumeration can stop now
    m_data.CompleteAdding();
}

お役に立てれば。ところで、昨夜これについてブログを投稿しました

アンドレ

于 2009-11-22T07:21:30.253 に答える
9

短い答え、いいえ。foreach一度に 1 つの列挙型に対してのみ機能します。

ただし、並列列挙型を 1 つに結合すると、結合したものを超えることができますforeach。これを行うための簡単な組み込みの方法は知りませんが、次の方法で機能するはずです(テストしていませんが):

public IEnumerable<TSource[]> Combine<TSource>(params object[] sources)
{
    foreach(var o in sources)
    {
        // Choose your own exception
        if(!(o is IEnumerable<TSource>)) throw new Exception();
    }

    var enums =
        sources.Select(s => ((IEnumerable<TSource>)s).GetEnumerator())
        .ToArray();

    while(enums.All(e => e.MoveNext()))
    {
        yield return enums.Select(e => e.Current).ToArray();
    }
}

foreach次に、返された列挙型を上書きできます。

foreach(var v in Combine(en1, en2, en3))
{
    // Remembering that v is an array of the type contained in en1,
    // en2 and en3.
}
于 2009-02-07T05:53:17.930 に答える
3

Zooba の回答は適切ですが、「一度に 2 つの配列を反復処理する方法」の回答も参照してください。

于 2009-02-07T07:21:09.203 に答える
3

.NET4 Parallel ライブラリから EachParallel() の実装を作成しました。.NET 3.5 と互換性があります: C# 3.5 での並列 ForEach ループ 使用法:

string[] names = { "cartman", "stan", "kenny", "kyle" };
names.EachParallel(name =>
{
    try
    {
        Console.WriteLine(name);
    }
    catch { /* handle exception */ }
});

実装:

/// <summary>
/// Enumerates through each item in a list in parallel
/// </summary>
public static void EachParallel<T>(this IEnumerable<T> list, Action<T> action)
{
    // enumerate the list so it can't change during execution
    list = list.ToArray();
    var count = list.Count();

    if (count == 0)
    {
        return;
    }
    else if (count == 1)
    {
        // if there's only one element, just execute it
        action(list.First());
    }
    else
    {
        // Launch each method in it's own thread
        const int MaxHandles = 64;
        for (var offset = 0; offset < list.Count() / MaxHandles; offset++)
        {
            // break up the list into 64-item chunks because of a limitiation             // in WaitHandle
            var chunk = list.Skip(offset * MaxHandles).Take(MaxHandles);

            // Initialize the reset events to keep track of completed threads
            var resetEvents = new ManualResetEvent[chunk.Count()];

            // spawn a thread for each item in the chunk
            int i = 0;
            foreach (var item in chunk)
            {
                resetEvents[i] = new ManualResetEvent(false);
                ThreadPool.QueueUserWorkItem(new WaitCallback((object data) =>
                {
                    int methodIndex = (int)((object[])data)[0];

                    // Execute the method and pass in the enumerated item
                    action((T)((object[])data)[1]);

                    // Tell the calling thread that we're done
                    resetEvents[methodIndex].Set();
                }), new object[] { i, item });
                i++;
            }

            // Wait for all threads to execute
            WaitHandle.WaitAll(resetEvents);
        }
    }
}
于 2009-12-18T22:06:22.037 に答える
0

これはうまくいきますか?

public static class Parallel
{
    public static void ForEach<T>(IEnumerable<T>[] sources,
                                  Action<T> action)
    {
        foreach (var enumerable in sources)
        {
            ThreadPool.QueueUserWorkItem(source => {
                foreach (var item in (IEnumerable<T>)source)
                    action(item);
            }, enumerable);
        }
    }
}

// sample usage:
static void Main()
{
    string[] s1 = { "1", "2", "3" };
    string[] s2 = { "4", "5", "6" };
    IEnumerable<string>[] sources = { s1, s2 };
    Parallel.ForEach(sources, s => Console.WriteLine(s));
    Thread.Sleep(0); // allow background threads to work
}

C# 2.0 の場合、上記のラムダ式をデリゲートに変換する必要があります。

注: このユーティリティ メソッドは、バックグラウンド スレッドを使用します。フォアグラウンド スレッドを使用するように変更することもできます。おそらく、すべてのスレッドが終了するまで待ちたいと思うでしょう。その場合は、sources.Length - 1スレッドを作成し、現在実行中のスレッドを最後の (または最初の) ソースに使用することをお勧めします。

(スレッドが終了するのを待つことをコードに含めたいと思っていますが、その方法がまだわからなくて申し訳ありません。使用する必要があると思いますaWaitHandle Thread.Join().)

于 2009-02-07T08:15:51.387 に答える