2010-03-29 16 views

risposta

51

Ogni volta che si desidera controllare l'esecuzione di più thread nella propria app. Anche se questo non significa solo che solo un thread incrementa il contatore; ma lascia che i thread inizino/si fermino o si interrompano su un evento.

Vedi WaitHandles - Auto/ManualResetEvent and Mutex

--EDIT--

WaitHandle s sono il meccanismo di che si "usa" per controllare l'esecuzione dei fili. Non si tratta di maniglie non accessibili all'interno di un thread; si tratta di usarli all'interno del thread.

Questo può essere un esempio di grasso, ma per favore abbiate pazienza con me; Pensa, una signora dà cinque diversi fischietti tonificati a cinque ragazze e dice loro di suonare il fischietto ogni volta che si verifica something; il processo prevede che ogni ragazza suoni un fischio, e la signora saprebbe chi ha suonato il fischietto.

Ora, non si tratta di condividere-i-fischietti tra di loro, si tratta, probabilmente per la signora, di usarli per "controllare" l'esecuzione o il processo su come le ragazze suonerebbero il fischietto.

Pertanto, tecnicamente il processo potrebbe essere quella di:

  1. creare un evento di attesa (oggetto ManualResetEvent)
  2. registro delle manifestazioni, WaitHandle.WaitAny(events);
  3. Dopo aver finito il funzionamento eseguendo nel tuo thread, il .Set() , che direbbe al WaitHandle che "I am done!".

Ad esempio, considerare l'esempio dal collegamento fornito. Ho aggiunto i passaggi per farti capire la logica. Questi non sono i passaggi hardcoded, ma solo così puoi capire.

class Test 
{ 
    static void Main() 
    { 
    //STEP 1: Create a wait handle 
     ManualResetEvent[] events = new ManualResetEvent[10];//Create a wait handle 
     for (int i=0; i < events.Length; i++) 
     { 
      events[i] = new ManualResetEvent(false); 
      Runner r = new Runner(events[i], i); 
      new Thread(new ThreadStart(r.Run)).Start(); 
     } 

    //STEP 2: Register for the events to wait for 
     int index = WaitHandle.WaitAny(events); //wait here for any event and print following line. 

     Console.WriteLine ("***** The winner is {0} *****", 
          index); 

     WaitHandle.WaitAll(events); //Wait for all of the threads to finish, that is, to call their cooresponding `.Set()` method. 

     Console.WriteLine ("All finished!"); 
    } 
} 


class Runner 
{ 
    static readonly object rngLock = new object(); 
    static Random rng = new Random(); 

    ManualResetEvent ev; 
    int id; 

    internal Runner (ManualResetEvent ev, int id) 
    { 
     this.ev = ev;//Wait handle associated to each object, thread in this case. 
     this.id = id; 
    } 

    internal void Run() 
    { 
    //STEP 3: Do some work 
     for (int i=0; i < 10; i++) 
     { 
      int sleepTime; 
      // Not sure about the thread safety of Random... 
      lock (rngLock) 
      { 
       sleepTime = rng.Next(2000); 
      } 
      Thread.Sleep(sleepTime); 
      Console.WriteLine ("Runner {0} at stage {1}", 
           id, i); 
     } 

    //STEP 4: Im done! 
     ev.Set(); 
    } 
} 
+0

WaitAll() - utilizzato per attendere che tutti gli handle di un set siano liberi/segnalati ... Significa che Handles non sarà accessibile ad altri thread fino a quando non saranno rilasciati (altri thread dovrebbero attendere per queste maniglie nel set)? – DotNetBeginner

+1

@DotNetBeginner: 'Significa che Handles non sarà accessibile ad altri thread'; per favore vedi il mio post aggiornato, un esempio aggiunto solo per rispondere a questa domanda. –

+0

vale la pena dire che 'ev.Set()' meglio essere inserito nel blocco 'finally' –

6

L'idea alla base dei metodi WaitAll e WaitAny è che sono utili quando si hanno molte attività che si desidera eseguire in parallelo.

Ad esempio, supponiamo di dover eseguire un processo che richiede l'elaborazione di 1000 elementi in una matrice che devono essere elaborati in parallelo. Un tipico core 2 Duo + hyperthreading ha solo 4 processori logici, quindi non ha senso avere più di 4 thread contemporaneamente (in realtà, ma è una storia per un'altra volta fingere e utilizzare il semplice modello "un thread per processore" per ora). Quindi 4 thread, ma 1000 articoli; cosa fai?

Un'opzione consiste nell'utilizzare il metodo WaitAny. Dai 4 thread, e ogni volta che il metodo WaitAny ti restituisce fai partire un altro, finché tutti i 1000 elementi non vengono messi in coda. Si noti che questo è un pessimo esempio per WaitAny, dal momento che si può anche solo dividere l'array in 250 blocchi di elementi. Si spera, tuttavia, che ti dia un'idea del tipo di situazione in cui WaitAny è utile. Ci sono altre situazioni simili in cui WaitAny può avere molto senso.

Ma ora torniamo allo scenario con 4 thread che elaborano 250 elementi dal tuo array di 1000 elementi. Con questa opzione, è possibile utilizzare il metodo WaitAll per attendere il termine dell'elaborazione.

7

È una classe astratta, non la si usa direttamente. Le classi derivate dal calcestruzzo sono ManualResetEvent, AutoResetEvent, Mutex e Semaphore. Classi importanti nella tua casella degli strumenti per implementare la sincronizzazione dei thread. Essi ereditano i metodi WaitOne, WaitAll e WaitAny, li si usa per rilevare che uno o più thread hanno segnalato la condizione di attesa.

Lo scenario di utilizzo tipico di Manual/AutoResetEvent consiste nel dire a un thread di uscire o consentire a un thread di segnalare che è passato a un punto di sequenza importante. Il semaforo ti aiuta a limitare il numero di thread che eseguono un'azione. O per implementare la sincronizzazione dei thread che non dovrebbe avere affinità con un particolare thread. Mutex è lì per assegnare la proprietà a una sezione di codice a un thread, l'istruzione di blocco è spesso applicabile anche lì.

I libri sono stati scritti a riguardo. Concurrent Programming in Windows di Joe Duffy è l'ultimo e il più grande. Fortemente consigliato se si contempla la scrittura di codice filettato.

27

WaitHandle è una classe di base astratta per i due handle di evento comunemente utilizzati: AutoResetEvent e ManualResetEvent.

Entrambe queste classi consentono a un thread di "segnalare" uno o più altri thread. Sono utilizzati per sincronizzare (o serializzare l'attività) tra i thread. Ciò è possibile utilizzando i metodi Set e WaitOne (o WaitAll). Per esempio:

filettatura 1:

// do setup work 

myWaitHandle.Set(); 

filettatura 2:

// do setup work 

myWaitHandle.WaitOne(); 

// this code will not continue until after the call to `Set` 
// in thread 1 completes. 

questo è un esempio molto rudimentale, e ci sono un sacco di loro sul web.L'idea di base è che WaitOne viene utilizzato per attendere un segnale da un altro thread che indica che è stato eseguito il . Nel caso di AsyncWaitHandle (che viene restituito dal richiamo di un delegato in modo asincrono), WaitOne consente di attendere il thread corrente fino al completamento dell'operazione asincrona.

Se non è impostato un AutoResetEvent o ManualResetEvent, le chiamate a WaitOne bloccano il thread chiamante fino alla chiamata di Set. Queste due classi differiscono solo per il fatto che "AutoResetEvent" disattiva "l'evento una volta completata una chiamata a WaitOne, in modo che le chiamate successive si blocchino di nuovo fino a quando viene chiamato Set. ManualResetEvent deve essere "disinserito" esplicitamente chiamando Reset.

WaitAll e WaitAny sono metodi statici sulla classe WaitHandle che consentono di specificare una serie di WaitHandles di aspettare. WaitAll bloccherà fino a tutte le delle maniglie fornite sono Set, mentre WaitAny bloccherà solo fino a uno di cui diventa Set.

1

Ci sono alcune risposte molto lunghe qui.Per chi cerca la risposta breve:

L'handle di attesa è un meccanismo per fare in modo che un thread attenda fino a quando un altro thread raggiunge un determinato punto.

È anche possibile avere diversi thread in attesa e/o diversi thread in attesa, quindi i metodi WaitOne, WaitAll e WaitAny. Ci sono anche diverse opzioni per la semantica disponibili scegliendo una di queste classi: Mutex, Semaphore, ManualResetEvent, AutoResetEvent che sono ben documentate.

Problemi correlati