Vedo che la domanda è stata riattivata con una taglia, chiedendo ora quali sono gli usi pratici yield
. Faccio un esempio dalla mia esperienza.
Come sappiamo, yield
forza il thread chiamante a rinunciare al processore su cui è in esecuzione in modo che sia possibile programmare l'esecuzione di un altro thread. Questo è utile quando il thread corrente ha terminato il suo lavoro per ora, ma vuole tornare rapidamente in primo piano e controllare se qualche condizione è cambiata. In che modo è diverso da una variabile di condizione? yield
consente al thread di tornare molto più rapidamente allo stato di esecuzione. Quando si attende una variabile di condizione, il thread viene sospeso e deve attendere che un thread diverso segnali che dovrebbe continuare.yield
fondamentalmente dice "consenti l'esecuzione di un thread diverso, ma permettimi di tornare a lavorare molto presto poiché mi aspetto che qualcosa cambi nel mio stato molto molto rapidamente". Questo suggerisce una rotazione impegnata, in cui una condizione può cambiare rapidamente ma la sospensione del thread comporterebbe un notevole calo delle prestazioni.
Ma basta balbettare, ecco un esempio concreto: il modello parallelo del fronte d'onda. Un esempio di base di questo problema è il calcolo delle singole "isole" di 1 in un array bidimensionale riempito con 0 e 1. Una "isola" è un gruppo di celle adiacenti l'una all'altra verticalmente o orizzontalmente:
1 0 0 0
1 1 0 0
0 0 0 1
0 0 1 1
0 0 1 1
Qui abbiamo due isole di 1: in alto a sinistra e in basso a destra.
Una soluzione semplice è eseguire un primo passaggio sull'intero array e sostituire i valori 1 con un contatore incrementale in modo tale che alla fine ogni 1 sia stato sostituito con il suo numero di sequenza nell'ordine principale delle righe:
1 0 0 0
2 3 0 0
0 0 0 4
0 0 5 6
0 0 7 8
Nella fase successiva, ogni valore viene sostituito dal minimo tra se stesso e i valori dei suoi vicini:
1 0 0 0
1 1 0 0
0 0 0 4
0 0 4 4
0 0 4 4
Ora possiamo facilmente determinare che abbiamo due isole.
La parte che vogliamo eseguire in parallelo è il passaggio in cui calcoliamo i minimi. Senza entrare troppo nei dettagli, ogni thread riceve le righe in modo intercalato e si basa sui valori calcolati dal thread che elabora la riga sopra. Pertanto, ogni thread deve rimanere leggermente indietro rispetto al thread che elabora la riga precedente, ma deve anche rimanere al passo entro un tempo ragionevole. Maggiori dettagli e un'implementazione sono presentati da me stesso in questo documento . Nota il cui utilizzo sleep(0)
è più o meno l'equivalente in C di yield
.
In questo caso è yield
stato utilizzato per forzare a turno ogni thread in pausa, ma poiché nel frattempo il thread che elabora la riga adiacente avanzerebbe molto rapidamente, una variabile di condizione si rivelerebbe una scelta disastrosa.
Come puoi vedere, yield
è un'ottimizzazione piuttosto fine. Usarlo nel posto sbagliato, ad esempio aspettando una condizione che cambia raramente, causerà un uso eccessivo della CPU.
Scusa per il lungo balbettio, spero di essere stato chiaro.