6

いくつかの入れ子になったループを高速化するために並列処理を使用しようとしていますが、正しい構文を取得するのに問題があります。ビットマップ内のピクセルが赤、白、または黒のいずれかであるかをカウントしようとしています。これらの値は、他の場所で列挙されています。

シリアル処理では、次のコードがあり、正常に動作します。

        Bitmap image = new Bitmap(@"Input.png");
        var colourCount = new int[3];

        for (var x = 0; x < image.Width; x++)
        {
            for (var y = 0; y < image.Height; y++)
            {
                switch (image.GetPixel(x, y).ToArgb())
                {
                    case (int)colours.red: colourCount[0]++; break;
                    case (int)colours.white: colourCount[1]++; break;
                    case (int)colours.black: colourCount[2]++; break;
                    default: throw new ArgumentOutOfRangeException(string.Format("Unexpected colour found: '{0}'", image.GetPixel(x, y).ToArgb()));
                }
            }
        }

以下のような共有変数を更新する Microsoft および Stackoverflow による並列 for ループのコードを見てきました。

        Parallel.For<int>(0, result.Count, () => 0, (i, loop, subtotal) =>
        {
            subtotal += result[i];
            return subtotal;
        },
            (x) => Interlocked.Add(ref sum, x)
        );

しかし、すべての例では共有変数として int などの単純な型を使用しているため、サイズ 3 の配列に書き込む構文がわかりません。私はこれにすべて間違って近づいていますか?

ところで、GetPixel は Bitmap.LockBits のようなものに比べてパフォーマンスが非常に遅いことを知っています。並列ループの原理を正しく理解しようとしているだけです。

4

1 に答える 1

5

のオーバーロードを使用すると、Parallel.Forスレッド ローカルの状態を維持できます。この場合、生成int[3]されるスレッドごとに配列を作成します。並列ループの各反復内で、ローカル配列のみを更新しlocalColourCountます。最後に、スレッドをリタイアするときは、各ローカル配列の結果をグローバル配列に集約しcolourCountます。ただし、これは共有データ構造であるため、アクセス中に相互排除を適用します。

Bitmap image = new Bitmap(@"Input.png");
var colourCount = new int[3];

Parallel.For(0, image.Width,

    // localInit: The function delegate that returns the initial state
    //            of the local data for each task.
    () => new int[3],

    // body: The delegate that is invoked once per iteration.
    (int x, ParallelLoopState state, int[] localColourCount) =>
    {
        for (var y = 0; y < image.Height; y++)
        {
            switch (image.GetPixel(x, y).ToArgb())
            {
                case (int)colours.red: localColourCount[0]++; break;
                case (int)colours.white: localColourCount[1]++; break;
                case (int)colours.black: localColourCount[2]++; break;
                default: throw new ArgumentOutOfRangeException(
                             string.Format("Unexpected colour found: '{0}'", 
                             image.GetPixel(x, y).ToArgb()));
            }
        }
    },

    // localFinally: The delegate that performs a final action
    //               on the local state of each task.
    (int[] localColourCount) =>
    {
        // Accessing shared variable; synchronize access.
        lock (colourCount)
        {
            for (int i = 0; i < 3; ++i)
                colourCount[i] += localColourCount[i];
        }
    });

このコードは、それがスレッドセーフであることを前提としてBitmap.GetPixelいますが、そうでない場合もあります。

もう 1 つ注意する必要があるのは、ArgumentOutOfRangeExceptionインスタンスが に結合されるAggregateExceptionことです。そのため、エラー処理コードを調整する必要があります。

于 2013-06-14T23:48:03.063 に答える