2010-02-15 16 views
13

Jestem zainteresowany poznawaniem programowania równoległego w C# .NET (nie wszystkim, co trzeba wiedzieć, ale podstawami i może kilkoma dobrymi praktykami), dlatego postanowiłem przeprogramować stary program moje, które nazywa się ImageSyncer. ImageSyncer to naprawdę prosty program, wystarczy przeskanować folder i znaleźć wszystkie pliki kończące się .jpg, a następnie oblicza nową pozycję plików w oparciu o datę ich zrobienia (parsowanie danych xif lub cokolwiek innego to jest nazwane). Po wygenerowaniu lokalizacji program sprawdza wszystkie istniejące pliki w tej lokalizacji, a jeśli istnieje, sprawdza ostatni czas zapisu zarówno pliku do skopiowania, jak i pliku "na swój sposób". Jeśli są równe, plik jest pomijany. Jeśli nie, to suma kontrolna md5 obu plików zostanie utworzona i dopasowana. Jeśli nie ma dopasowania, plik, który ma zostać skopiowany, otrzymuje nową lokalizację do skopiowania (na przykład, jeśli ma zostać skopiowany do "C: \ test.jpg", jest kopiowany do "C: \ test (1). jpg "zamiast). Wynik tej operacji jest zapełniany w kolejce typu struct, która zawiera dwa ciągi, oryginalny plik i pozycję do skopiowania. Następnie kolejka jest iterowana, dopóki nie jest pusta i pliki są kopiowane.Programowanie równoległe w C#

Innymi słowy istnieją 4 operacje:

1. Scan directory for jpegs 
2. Parse files for xif and generate copy-location 
3. Check for file existence and if needed generate new path 
4. Copy files 

A więc chcę przepisać ten program, aby to paralell i być w stanie wykonać kilka operacji w tym samym czasie, i zastanawiałem się, co najlepszym sposobem na osiągnięcie tego. Wymyśliłem dwa różne modele, które mogę wymyślić, ale żadna z nich nie może być w ogóle dobra. Pierwszym z nich jest zrównoleglenie 4 kroków starego programu, więc gdy pierwszy krok ma zostać wykonany, wykonuje się go na kilku wątkach, a gdy cały etap 1 zostanie zakończony, rozpoczyna się etap 2. Drugim (który uważam za bardziej interesujący, ponieważ nie mam pojęcia, jak to zrobić) jest stworzenie pewnego rodzaju modelu pracownika i konsumenta, więc gdy wątek zostanie zakończony w kroku 1, inny przejmie i wykona krok 2 w tym obiekt (lub coś w tym stylu). Ale, jak powiedziałem, nie wiem, czy którekolwiek z nich są dobre rozwiązania. Poza tym nie wiem zbyt wiele na temat programowania równoległego. Wiem, jak utworzyć wątek i jak sprawić, by wykonywał on funkcję przyjmującą obiekt jako jego jedyny parametr, a także użyłem klasy BackgroundWorker w jednym przypadku, ale nie jestem zaznajomiony z żadnym z nich. .

Wszelkie dane wejściowe będą mile widziane.

+9

To brzmi jak ciekawym zadaniem, ale ponieważ jest to prawdopodobnie IO powiązane, wiele wątków wszystkie uderzające w dysk najprawdopodobniej sprawią, że program będzie działać wolniej niż w przypadku użycia tylko jednego wątku. –

+0

Thanx, naprawdę nie uważałem tego. Ale myślę, że przynajmniej kroki 2 i 3 mogą skorzystać z kilku wątków, nie zgodziłbyś się? – Alxandr

+0

http://messagingbus.codeplex.com/ może pomóc –

Odpowiedz

2

Jest to odniesienie używam dla C# wątku: http://www.albahari.com/threading/

jako single PDF: http://www.albahari.com/threading/threading.pdf

Za drugie podejście:

pracowałem na niektórych producent/konsumentów wielowątkowych aplikacjach, gdzie każde zadanie jest jakiś kod, który pętli fo kiedykolwiek.Zewnętrzny "inicjator" uruchamia oddzielny wątek dla każdego zadania i inicjuje EventWaitHandle dla każdego zadania. Dla każdego zadania jest globalna kolejka, która może być używana do produkcji/konsumpcji danych wejściowych.

W twoim przypadku twój program zewnętrzny doda każdy katalog do kolejki dla zadania 1 i ustawi EventWaitHandler dla zadania 1. Zadanie 1 "obudziłoby się" ze swojego EventWaitHandler, uzyskało liczbę katalogów w swojej kolejce, a następnie, gdy liczba jest większa od 0, pobierz katalog z kolejki, zeskanuj wszystkie .jpgs i dodaj każdą lokalizację .jpg do drugiej kolejki i ustaw funkcję EventWaitHandle dla zadania 2. Zadanie 2 wczytuje dane wejściowe, przetwarza je, przekazuje dalej do kolejki dla zadania 3 ...

To może być trochę uciążliwe uzyskanie blokady do działa poprawnie (zasadniczo blokuję dostęp do kolejki, nawet coś tak prostego, jak uzyskanie jej liczby). .NET 4.0 powinien mieć struktury danych, które będą automatycznie obsługiwać kolejkę producenta/konsumenta bez blokad.

1

Interesujący problem. Wymyśliłem dwa podejścia. Pierwszy oparty jest na PLinq, a drugi oparty jest na Rx Framework.

Pierwsza z nich wykonuje iteracje między plikami równolegle. Drugi generuje asynchronicznie pliki z katalogu.

Oto jak to wygląda w znacznie uproszczonej wersji (Pierwsza metoda wymaga .NET 4.0, ponieważ używa PLinq)

string direcory = "Mydirectory"; 
    var jpegFiles = System.IO.Directory.EnumerateFiles(direcory,"*.jpg"); 


    // -- PLinq -------------------------------------------- 
    jpegFiles 
    .AsParallel() 
    .Select(imageFile => new {OldLocation = imageFile, NewLocation = GenerateCopyLocation(imageFile) }) 
    .Do(fileInfo => 
     { 
      if (!File.Exists(fileInfo.NewLocation) || 
       (File.GetCreationTime(fileInfo.NewLocation)) != (File.GetCreationTime(fileInfo.NewLocation))) 
       File.Copy(fileInfo.OldLocation,fileInfo.NewLocation); 
     }) 
    .Run(); 

    // ----------------------------------------------------- 


    //-- Rx Framework --------------------------------------------- 
    var resetEvent = new AutoResetEvent(false); 
    var doTheWork = 
    jpegFiles.ToObservable() 
    .Select(imageFile => new {OldLocation = imageFile, NewLocation = GenerateCopyLocation(imageFile) }) 
    .Subscribe(fileInfo => 
     { 
      if (!File.Exists(fileInfo.NewLocation) || 
       (File.GetCreationTime(fileInfo.NewLocation)) != (File.GetCreationTime(fileInfo.NewLocation))) 
      File.Copy(fileInfo.OldLocation,fileInfo.NewLocation); 
     },() => resetEvent.Set()); 

    resetEvent.WaitOne(); 
    doTheWork.Dispose(); 

    // ----------------------------------------------------- 
+0

PLinq wymaga .net 4.0, czy to nieprawda? – Alxandr

+0

Tak, wymaga to .Net 4.0 –

+0

+1 za podanie i podanie przykładu dla podejścia "Rx". –