2010-02-15 2 views
13

Sono interessato a conoscere la programmazione parallela in C# .NET (non come tutto quello che c'è da sapere, ma le basi e forse alcuni buoni-pratiche), quindi ho deciso di riprogrammare un vecchio programma mio che è chiamato ImageSyncer. ImageSyncer è un programma molto semplice, tutto ciò che fa è quello di eseguire la scansione attraverso un cartella e trovare tutti i file che terminano con .jpg, poi calcola la nuova posizione dei file in base alla data in cui sono state scattate (analisi dei xif-dati, o qualsiasi altra cosa è chiamato). Dopo un percorso è stato generato il programma verifica per eventuali file esistenti in quella posizione, e se uno esiste, guarda l'ultima scrittura-tempo sia il file da copiare e il file "a suo modo". Se sono uguali, il file viene saltato. In caso contrario, viene creato e abbinato un checksum md5 di entrambi i file. Se non v'è alcuna corrispondenza il file da copiare è dato una nuova posizione da copiare (per esempio, se doveva essere copiato in "C: \ test.jpg" è copiato in "C: \ test (1). jpg "invece). Il risultato di questa operazione è popolato in una coda di un tipo di struct che contiene due stringhe, il file originale e la posizione in cui copiarlo. Quindi quella coda viene iterata finché non è vuota e i file vengono copiati.Programmazione parallela in C#

In altre parole ci sono 4 operazioni:

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 

E quindi voglio riscrivere il programma per renderlo paralell ed essere in grado di eseguire molte delle operazioni, allo stesso tempo, e mi chiedevo cosa il modo migliore per raggiungere questo sarebbe. Mi sono inventato due modelli diversi a cui riesco a pensare, ma nessuno dei due potrebbe essere affatto valido. Il primo è quello di parallelizzare le 4 fasi del vecchio programma, in modo che quando si passo deve essere eseguito è fatto su più fili, e quando l'intera della fase 1 è terminato viene iniziata passaggio 2. L'altro (che trovo più interessante perché non ho idea di come farlo) è quello di creare una sorta di lavoratore e il modello di consumo, in modo da quando un thread è finito con la fase 1 un altro prende il sopravvento e si esibisce fase 2 in quel oggetto (o qualcosa del genere). Ma come detto, non so se nessuna di queste sia una buona soluzione. Inoltre, non conosco molto della programmazione parallela. So come creare un thread e come farlo eseguire una funzione prendendo in un oggetto come il suo unico parametro, e ho anche usato la classe BackgroundWorker in una occasione, ma non sono così familiare con nessuno di loro .

Qualsiasi input sarebbe apprezzato.

+9

Questo suona come un compito interessante, ma dal momento che è probabile che sia IO legato, più thread che sbattono sul disco molto probabilmente faranno funzionare il programma _slower_ piuttosto che se tu abbia appena usato un thread. –

+0

Grazie, non ho davvero riflettuto su questo. Ma penso che almeno i passaggi 2 e 3 potrebbero trarre vantaggio dall'utilizzo di più thread, non sei d'accordo? – Alxandr

+0

http://messagingbus.codeplex.com/ può aiutare –

risposta

2

Questo è il riferimento che uso per C# discussione: http://www.albahari.com/threading/

Come singolo PDF: http://www.albahari.com/threading/threading.pdf

Per il secondo approccio:

ho lavorato su alcune applicazioni multithread produttore/consumatore, dove ogni attività è un codice che loop fo mai.Un "inizializzatore" esterno avvia un thread separato per ogni attività e inizializza un EventWaitHandle per ogni attività. Per ogni attività è una coda globale che può essere utilizzata per produrre/consumare input.

Nel tuo caso, il tuo programma esterno aggiungerà ogni directory alla coda per Task1 e Imposta EventWaitHandler per Task1. L'attività 1 si "sveglia" dal suo EventWaitHandler, ottiene il conteggio delle directory nella sua coda e quindi mentre il conteggio è maggiore di 0, ottiene la directory dalla coda, esegue la scansione di tutti i .jpg e aggiunge ogni posizione .jpg ad una seconda coda, e impostare l'EventWaitHandle per l'attività 2. attività 2 legge il suo ingresso, lo elabora, inoltra ad una coda per Task 3 ...

può essere un po 'un dolore che ottiene tutto il bloccaggio a funziona bene (praticamente blocco qualsiasi accesso alla coda, anche qualcosa di semplice come ottenere il suo conteggio). Si suppone che .NET 4.0 abbia strutture dati che supporteranno automaticamente una coda produttore/consumatore senza blocchi.

1

Interessante problema. Mi sono inventato due approcci. Il primo è basato su PLinq e il secondo è basato su te Rx Framework.

Il primo scorre in parallelo i file. Il secondo genera in modo asincrono i file dalla directory.

Ecco come appare come in una versione molto semplificata (Il primo metodo non richiede .Net 4.0 in quanto utilizza 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 richiede .net 4.0, non è corretto? – Alxandr

+0

Sì, richiede .Net 4.0 –

+0

+1 per menzionare e fornire un esempio per l'approccio "Rx". –