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是线程安全的,这可能是也可能不是。

您需要注意的另一件事是任何ArgumentOutOfRangeException实例都将合并到一个AggregateException中,因此您需要调整错误处理代码。

于 2013-06-14T23:48:03.063 回答