Parallel for loop in C # with a shared variable

I'm trying to use parallel processing to speed up a couple of nested loops, but it's hard for me to get the syntax correctly. I am trying to calculate how many pixels in a bitmap will be red, white or black, the values ​​that I have in an enumeration elsewhere.

In sequential processing, I have the following code that works fine:

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())); } } } 

I saw code for parallel loops of Microsoft and Stackoverflow that update a shared variable, like below:

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

But all the examples use a simple type such as int as a shared variable, and I just can't figure out the syntax for writing to my array of size three. Am I approaching this all wrong?

By the way, I know in terms of performance that GetPixel is very slow compared to something like Bitmap.LockBits, I'm just trying to get the principle of parallel loops.

+6
source share
1 answer

You can use Parallel.For overload, which allows you to maintain the local state of the stream. In this case, we create an int[3] array for each spawned stream. In each iteration of the parallel loop, we update only the localColourCount local array. Finally, when the stream needs to be deleted, we combine the results of each local array into a global one, colourCount ; however, since this is a general data structure, we provide mutual exclusion when accessing it.

 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]; } }); 

This code assumes Bitmap.GetPixel is thread safe, which may or may not be.

Another thing you need to pay attention to is that any instances of ArgumentOutOfRangeException are combined into an AggregateException , so you will need to set up error handling code.

+4
source

Source: https://habr.com/ru/post/947345/


All Articles