2013-06-14 17 views
6

Próbuję użyć przetwarzania równoległego, aby przyspieszyć kilka zagnieżdżonych pętli, ale mam problemy z uzyskaniem prawidłowej składni. Próbuję uzyskać liczbę pikseli w bitmapie: czerwoną, białą lub czarną, wartości, które mam w wyliczeniu gdzie indziej.Równoległa dla pętli w C# ze wspólną zmienną

W przetwarzania szeregowego Mam następujący kod, który działa prawidłowo:

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

Widziałem kod równolegle do pętli przez Microsoft i od Stackoverflow że zaktualizować zmienną udostępnionego takie jak poniżej:

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

Ale wszystkie przykłady używają prostego typu, takiego jak int jako zmienna dzielona i po prostu nie mogę znaleźć składni, aby napisać do mojej tablicy wielkości 3. Czy podchodzę do tego źle?

Nawiasem mówiąc, wiem pod względem wydajności, że GetPixel jest bardzo powolny w porównaniu do czegoś w rodzaju Bitmap.LockBits, po prostu próbuję uzyskać zasadę równoległych pętli w prawo.

Odpowiedz

4

Można użyć przeciążenia z Parallel.For, które pozwala zachować stan lokalny wątku. W takim przypadku tworzymy tablicę int[3] dla każdego wątku, który jest spawnowany. W ramach każdej iteracji pętli równoległej aktualizujemy tylko lokalną tablicę , localColourCount. Wreszcie, kiedy wątek ma zostać wycofany, agregujemy wyniki każdej lokalnej tablicy do globalnej, colourCount; Jednak ponieważ jest to wspólna struktura danych, wymuszamy wzajemne wykluczenie podczas uzyskiwania do niej dostępu.

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

Kod ten zakłada, że ​​gwint jest Bitmap.GetPixel bezpieczne, które mogą być lub mogą nie być.

Inną rzeczą, na którą należy zwrócić uwagę, jest to, że wszystkie instancje ArgumentOutOfRangeException zostaną połączone w jeden obiekt, dlatego konieczne będzie dostosowanie kodu obsługi błędów.

+0

Właściwie nie potrzebujesz 'blokady' w' localFinally'. Zamiast tego możesz użyć 'Interlocked.Add()' w swojej pętli. Chociaż użycie 'lock' sprawia, że ​​jest to bardziej poprawne. – svick

+0

@svick: Wiele inwokacji 'Interlocked.Add' może być mniej wydajne niż pojedynczy' lock'. (Zdecydowanie miałoby to miejsce, gdyby "colourCount" składało się z kilkudziesięciu elementów, nie na pewno dla 3.) Per [Albahari] (http://www.albahari.com/threading/part4.aspx): "All of" Interlocked "Metody metody generują pełne ogrodzenie". Powtarzające się obciążenie związane z unieważnieniem pamięci podręcznej może przekraczać koszt pojedynczej blokady. – Douglas

+0

Dzięki za opublikowanie tego, teraz ma dla mnie dużo więcej sensu. Masz rację sugerując, że GetPixel nie jest bezpieczny dla wątków, w rzeczywistości dostęp do dowolnej właściwości obrazu lub metod wydaje się być nie-nie, więc muszę wymyślić na odwrót, ale mam ale sama pętla działa dobrze. Dzięki jeszcze raz! –