Guide C# – Sincronizzazione dei Thread

eccoci con la 3 parte del piccolo corso sui thread in c#. un altro problema che può verificarsi molto spesso è il voler compiere un’azione asincrona ma non volere che il thread principale continui finchè questi non abbia finito l’operazione, è in pratica un’operazione di sincronizzazione fra thread. c’è da chiedersi allora il perchè non fare direttamente un’operazione sincrona… i motivi ci sono, primo fra tutti il non voler intasare troppo il thread principale oppure voler fare altre operazioni prima di mettersi in attesa… ecco perciò che ci viene in aiuto l’oggetto EventWaitHandle.

posizioniamoci prima della richiesta asincrona e assegnamo un nuovo oggetto all’istanza:

EventWaitHandle waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);

a questo punto chiamiamo il nuovo thread come da codice visto in precedenza.

ThreadStart entry = new ThreadStart(CheckFile);
Thread thread = new Thread(entry);
thread.Start();

facciamo tutto quello che vogliamo fare e poi mettiamoci in pausa in attesa della fine del thread, magari con un timeout di 20 secondi ad esempio:

if (!waitHandle.WaitOne(20000, true))
{
//gestione dell'errore
}

adesso, in un punto qualsiasi della vita del thread parallelo, nel momento in cui vogliamo risvegliare il thread principale, settiamo l’handle da noi messo in pausa:

waitHandle.Set();

a questo punto il thread principale si sveglierà e noi saremmo in grado di continuare.

di seguito un codice di esempio:

ConsoleApplication3.zip

This entry was posted in Linguaggi .Net, Programmazione and tagged , , , , , , , , , , , , , . Bookmark the permalink.

14 Responses to Guide C# – Sincronizzazione dei Thread

  1. Gianlu says:

    Ciao, grazie per la tua soluzione mi è tornata utile.
    Personalmente l’ho modificata un po’ avendo un Main Thread che fa partire n Threads figli ho risolto in questo modo

    //nel main thread
    EventWaitHandle waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
    
    for (int i= 0 ;i< n; i++)
    {
       lancio threadFigli(waitHandle)
    }
    
    for (int i= 0 ;i< n; i++)
    {
        waitHandle.WaitOne()
    }
    
    //all'interno di ogni Thread figlio
    //work
    
    waitHandle.set();
    

    in questo modo il main thread si mette in attesa tante volte quanti thread figli partono e procede mano mano che ritornano.

    Secondo te è una buona soluzione?

    ciao!

  2. Denis says:

    Ciao Gianlu. Guarda come dico spesso negli articoli, ogni soluzione è ottima per quello che si vuol fare, dipende dallo stile di programmazione, dal problema iniziale e soprattutto dalle conoscenze del programmatore.

    Nel tuo caso particolare, mi pare di aver capito che hai la necessità di sincronizzare l’avvio ed il termine di N thread di cui il Main è il padre. Una volta terminati tutti i thread, allora il Main tornerà a svolgere il suo lavoro.

    A questo punto direi che in linea di massima la tua soluzione funziona, tuttavia ti faccio un paio di appunti:

    • Nel caso ti vada in dead-lock un thread, senza il timeout nella WaitOne rischi che ti si inchiodi il programma
    • Ti ricordo che per lavorare e sincronizzare thread multipli esiste la classe ThreadPool che talvolta può semplificare la vita

    Grazie per il tuo contributo! ciao emoticon

  3. Ervis says:

    Ciao, ho letto l’articolo e mi sembra molto utile ma non è ancora quello che vorrei fare.
    Ti spiego brevemente: avrei bisogno di lanciare uno stesso metodo con parametri diversi da n thread diversi (per esempio 3 thread) e attendo un valore di ritorno dal metodo. In base a questo valore decido se rieseguire il thread con gli stessi parametri o con parametri diversi.
    Hi qualche suggerimento in merito?
    grazie per la disponibilità

  4. Denis says:

    Ervis:

    Ciao, ho letto l’articolo e mi sembra molto utile ma non è ancora quello che vorrei fare.
    Ti spiego brevemente: avrei bisogno di lanciare uno stesso metodo con parametri diversi da n thread diversi (per esempio 3 thread) e attendo un valore di ritorno dal metodo. In base a questo valore decido se rieseguire il thread con gli stessi parametri o con parametri diversi.
    Hi qualche suggerimento in merito?
    grazie per la disponibilità

    Ciao Ervis! Scusami ma non ho ben capito la tua domanda: chiamare un metodo con “parametri diversi” non ha propriamente senso, perchè un metodo è fisso e perciò i parametri (la signature del metodo) è dichiarata a priori in fase di codifica, a run-time non si può modificare… questo soprattutto considerando che tu vorresti avere N possibilità. Sei sicuro che sia la via risolutiva migliore?

    Fammi sapere. ciao emoticon

  5. Ervis says:

    ciao grazie per la risposta innanzitutto.
    scusami ma mi sono espresso male io, chiaramente non si può fare quello che intendevo è “parametri con valori diversi”.
    mi spiego meglio, il mio è un metodo semplicissimo es.:
    bool MioMetodo(int param1, string param2)
    { … }
    io vorrei chiamare questo metodo ad esempio da 3 thread diversi e appena ricevo il valore booleano di ritorno faccio le mie considerazioni e decido quale operazione eseguire successivamente.
    il mio obiettivo è quello di eseguire una lista di richieste in più breve tempo. non so se sono riuscito a spiegarmi…
    grazie ancora

  6. Denis says:

    Ervis:

    ciao grazie per la risposta innanzitutto.
    scusami ma mi sono espresso male io, chiaramente non si può fare quello che intendevo è “parametri con valori diversi”.
    mi spiego meglio, il mio è un metodo semplicissimo es.:
    bool MioMetodo(int param1, string param2)
    { … }
    io vorrei chiamare questo metodo ad esempio da 3 thread diversi e appena ricevo il valore booleano di ritorno faccio le mie considerazioni e decido quale operazione eseguire successivamente.
    il mio obiettivo è quello di eseguire una lista di richieste in più breve tempo. non so se sono riuscito a spiegarmi…
    grazie ancora

    Ciao di nuovo! Da quello che mi dici non c’è nulla di particolarmente complicato:

    1. crei gli N thread come spiegato qui
    2. dopodichè all’interno della funzione del thread (che è comune a tutti) chiami il tuo metodo con i 2 parametri stringa e fai i tuoi controlli

    Fammi sapere se era questo che volevi sapere.

    A presto! ciao emoticon

  7. Ervis says:

    grazie ancora per la risposta, mi manca ancora un pezzo, facciamo un esempio:
    ho una lista di operazioni da fare che mi chiedono la consultazione verso dei dispositivi. per es. 100 operazioni e ho 3 dispositivi attaccati al pc, quindi uso 3 thread per consumare più velocemente la mia lista anzichè uno per volta
    le voglio fare con un ciclo(non so se sia la strada giusta):
    while (i<=100)
    {
    // come faccio a chiamare il thread con i 2 parametri in ingresso
    // ed aspettarmi una risposta dal metodo chiamato (in questo caso
    // booleano)? bisogna considerare che quando un thread finisce lo
    // faccio ripatire (o ne creo uno nuovo non so) per poter eseguire
    // l'operazione successiva finchè non sono arrivato a 100

    }
    —————————————
    metodo da richiamare:
    bool MioMetodo(int param) { … }

    spero di non aver complicato ulteriormente le cose…

  8. Denis says:

    Ervis:

    grazie ancora per la risposta, mi manca ancora un pezzo, facciamo un esempio:
    ho una lista di operazioni da fare che mi chiedono la consultazione verso dei dispositivi. per es. 100 operazioni e ho 3 dispositivi attaccati al pc, quindi uso 3 thread per consumare più velocemente la mia lista anzichè uno per volta
    le voglio fare con un ciclo(non so se sia la strada giusta):
    while (i<=100)
    {
    // come faccio a chiamare il thread con i 2 parametri in ingresso
    // ed aspettarmi una risposta dal metodo chiamato (in questo caso
    // booleano)? bisogna considerare che quando un thread finisce lo
    // faccio ripatire (o ne creo uno nuovo non so) per poter eseguire
    // l'operazione successiva finchè non sono arrivato a 100

    }
    —————————————
    metodo da richiamare:
    bool MioMetodo(int param) { … }

    spero di non aver complicato ulteriormente le cose…

    Hmm sembra che tu abbia un pò di confusione. Un thread non è una cosa che tu puoi chiamare a tuo piacimento per fargli fare quello che vuoi, bensì è un’entità attiva che nasce, vive e muore. Mi spiego meglio: tu crei il thread, gli fai eseguire un compito ed infine questo muore. Una volta che è morto non puoi farlo “rivivere”, ma devi crearne un altro che a sua volta farà qualcos’altro. Quello che farei io per risolvere il tuo problema (in base a quello che mi hai detto), è suddividere il lavoro tra i 3 thread, del tipo:

    1. 1 lista di operazioni (lunga es. 100) condivisa tra tutti;
    2. ogni thread, una volta creato, ha un ciclo interno che va avanti fintanto che la lista non è vuota;
    3. all’interno del ciclo il thread compie l’operazione N-esima, chiamando la funzione con i parametri che dicevi;
    4. completato ogni ciclo ogni thread andrà a richiedere alla lista una nuova operazione da svolgere e, nel caso queste siano finite, uscirà dal ciclo, morendo.

    Fammi sapere se può andar bene.

    A presto ciao emoticon

  9. Ervis says:

    è vero non ci avevo pensato, o meglio mi ero fissato del fatto che il ciclo glielo facevo, non all’interno del thread come giustamente dici tu (se ho capito bene), ma i thread stessi erano all’interno del cliclo.
    con questa soluzione che mi sembra possa andare bene ok emoticon mi sono venuti dei dubbi: wall emoticon
    1. premetto che la lista delle operazioni da fare la prendo con una select dal database, non è che potrei correre il pericolo che 2 thread diversi mi facciano la stessa operazione dato che l’operzione stessa, prima che sia finita dura dai 2 ai 3 secondi? dovrei mettere un lock quando interrogo il database? o dovrei segnare sul db la riga come impegnata? o non so??
    2. come faccio a chiamare un metodo (es. bool MioMetodo(int param)) dal mio thread in modo che mi rotorni un valore, in questo caso booleano?
    3. il metodo MioMetodo dovrebbe scrivere dei log sullo stesso file che però in questo caso anrebbe in conflitto dato che più thread potrebbero scrivere contemporaneamente…come posso fare? creo n file diversi quanti sono i thread?

  10. Denis says:

    Ervis:

    1. premetto che la lista delle operazioni da fare la prendo con una select dal database, non è che potrei correre il pericolo che 2 thread diversi mi facciano la stessa operazione dato che l’operzione stessa, prima che sia finita dura dai 2 ai 3 secondi? dovrei mettere un lock quando interrogo il database? o dovrei segnare sul db la riga come impegnata? o non so??
    2. come faccio a chiamare un metodo (es. bool MioMetodo(int param)) dal mio thread in modo che mi rotorni un valore, in questo caso booleano?
    3. il metodo MioMetodo dovrebbe scrivere dei log sullo stesso file che però in questo caso anrebbe in conflitto dato che più thread potrebbero scrivere contemporaneamente…come posso fare? creo n file diversi quanti sono i thread?

    Allora, una cosa alla volta:

    1. Modificare un Database in concorrenza non è mai cosa buona, quindi quello che ti consiglio di fare io è di leggere un’unica volta (all’inizio) le operazioni dal DB ed inserirle in un costrutto logico in memoria tipo una QueueList che in automatico ti toglie dalla lista l’elemento appena lo leggi (ultracomodo nella tua situazione);
    2. Il thread è una classe che può eseguire del codice… nel codice ci sarà la chiamata al metodo… mi spiego?
    3. Per la scrittura concorrente potresti affidarti sempre ad una classe che è abilitata da semafori per scrivere su file in modo che ogni thread se chiama il metodo della classe di log e questo è impegnato viene automaticamente bloccato fintanto che l’altro thread non ha terminato di usarla…

    Spero di essere stato chiaro, purtroppo il problema della concorrenza non è subito facile… fammi sapere!

    ciao emoticon

  11. Ervis says:

    Denis:

    Allora, una cosa alla volta:

    Modificare un Database in concorrenza non è mai cosa buona, quindi quello che ti consiglio di fare io è di leggere un’unica volta (all’inizio) le operazioni dal DB ed inserirle in un costrutto logico in memoria tipo una QueueList che in automatico ti toglie dalla lista l’elemento appena lo leggi (ultracomodo nella tua situazione);
    Il thread è una classe che può eseguire del codice… nel codice ci sarà la chiamata al metodo… mi spiego?
    Per la scrittura concorrente potresti affidarti sempre ad una classe che è abilitata da semafori per scrivere su file in modo che ogni thread se chiama il metodo della classe di log e questo è impegnato viene automaticamente bloccato fintanto che l’altro thread non ha terminato di usarla…

    Spero di essere stato chiaro, purtroppo il problema della concorrenza non è subito facile… fammi sapere!

    ciao emoticon

    grazie per il suggerimento della QueueList lo terrò in considerazione
    Il thread è una classe che può eseguire del codice… nel codice ci sarà la chiamata al metodo… mi spiego? ok ora ho capito sisi emoticon
    Per la scrittura concorrente potresti affidarti sempre ad una classe che è abilitata da semafori… avresti mica qualche piccolo esempio?

    grazie di tutto, mi hai schiarito le idee un bel po’

  12. Denis says:

    Ervis:

    grazie per il suggerimento della QueueList lo terrò in considerazione
    Il thread è una classe che può eseguire del codice… nel codice ci sarà la chiamata al metodo… mi spiego? ok ora ho capito sisi emoticon
    Per la scrittura concorrente potresti affidarti sempre ad una classe che è abilitata da semafori… avresti mica qualche piccolo esempio?

    grazie di tutto, mi hai schiarito le idee un bel po’

    Certo, puoi dare un’occhiata qui che spiega molto bene cosa devi fare: Istruzione Lock (C#)

    In pratica fai una classe Log con all’interno un metodo log(stringa) al cui interno fai un lock su un oggetto privato della classe al cui interno a sua volta fai la scrittura su file. Al resto ci pensa il framework.

    A presto! ciao emoticon

  13. Ervis says:

    gentilissimo
    grazie mille ciao emoticon

  14. Denis says:

    Di nulla figurati! A presto!

Lascia un Commento

L'indirizzo email non verrà pubblicato. I campi obbligatori sono contrassegnati *

*

È possibile utilizzare questi tag ed attributi XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>