L'unione esiste perché i thread devono comunicare tra loro. Dopo che è avvenuta la modifica, il thread chiamato può modificare il valore di una variabile globale, a cui il thread chiamante deve accedere. Questa è una forma di sincronizzazione.
Questo articolo spiega due modi per unire i thread. Inizia con un'illustrazione di cosa sia un thread.
Contenuto dell'articolo
Filo
Considera il seguente programma:
#includere
Utilizzo dello spazio dei nomi std;
void fn2 ()
cout << "function 2" << '\n';
void fn1 ()
cout << "function 1" << '\n';
int main ()
/ * Alcune affermazioni */
restituzione 0;
fn1 (), fn2 () e main () sono funzioni di alto livello, sebbene main () sia una funzione chiave. Tre thread possono essere ottenuti da queste tre funzioni di alto livello. Il seguente programma breve molto semplice, è un thread naturale:
#includere
Utilizzo dello spazio dei nomi std;
int main ()
/ * Alcune affermazioni */
restituzione 0;
La funzione principale () si comporta come un thread. Può essere considerato come il thread principale. Non è necessario incapsulare in alcuna istanza dalla classe thread. Quindi, il programma precedente con funzioni di alto livello che include la funzione principale () è ancora un thread. Il seguente programma mostra come le due funzioni, fn1 () e fn2 () possono essere convertite in thread, ma senza alcuna istruzione di join:
#includere
#includere
#includere
Utilizzo dello spazio dei nomi std;
String Globl1 = String ("So");
String Globl2 = String ("Sia!");
void fn2 (string ST2)
String Globl = Globl1 + ST2;
cout << globl << endl;
void fn1 (string ST1)
Globl1 = "Sì. " + ST1;
Discussione Thr2 (Fn2, Globl2);
int main ()
Discussione Thr1 (Fn1, Globl1);
/ * Alcune affermazioni */
restituzione 0;
Il programma inizia con l'inclusione della libreria iostream per l'oggetto Cout. Quindi è inclusa la libreria di thread. Compresa la libreria di thread è un must; in modo che il programmatore semplicemente istanzia un oggetto thread dalla classe thread usando una funzione di livello superiore, ad eccezione della funzione principale ().
Successivamente, la libreria di stringa è inclusa. Questa libreria semplifica l'uso dei letterali delle stringhe. La prima dichiarazione nel programma insiste sul fatto che qualsiasi nome utilizzato provenga dallo spazio dei nomi standard C ++ se non diversamente indicato.
Le due dichiarazioni successive dichiarano due oggetti di stringa globale con i loro letterali. Gli oggetti di stringa sono chiamati Globl1 e Globl2. C'è la funzione fn1 () e la funzione fn2 (). Il nome della funzione fn2 () verrà utilizzato come uno degli argomenti per istanziare il thread, thr2, dalla classe thread. Quando una funzione viene istanziata in questo modo, viene chiamata la funzione; E esegue. Quando viene chiamata la funzione fn2 (), concatena i letterali della corda di Globl1 e Globl2 per avere “Quindi, sia, sia questo!". Globl2 è l'argomento di fn2 ().
Il nome della funzione fn1 () è usato come argomento per l'istanziazione del thread, thr1, dalla classe thread. Durante questa istanza, fn1 () viene chiamato. Quando viene chiamato, precede la stringa, “Quindi, sia!"Con" Sì. ", per avere" Sì. Così sia!", Che è l'output per l'intero programma di thread.
La funzione principale (), che è il thread principale (), istanzia il thread, thr1 dalla classe del filo, con gli argomenti FN1 e GLOBL1. Durante questa istanza, fn1 () viene chiamato. La funzione, fn1 () è il thread efficace per l'oggetto, thr1. Quando viene chiamata una funzione, dovrebbe funzionare dall'inizio alla sua fine.
thr1, che è effettivamente fn1 (), istanzia il filo, thr2, dalla classe del filo, con gli argomenti fn2 e globl2. Durante questa istanza, fn2 () viene chiamato. La funzione, fn2 () è il thread efficace per l'oggetto, thr2. Quando viene chiamata una funzione, dovrebbe funzionare dall'inizio alla sua fine.
Se il lettore utilizza, il compilatore G ++, può testare questo programma di thread, per la compilation C ++ 20, con il seguente comando:
g ++ -std = c ++ 2a temp.cpp -lpthread -o temp
L'autore ha fatto questo; ha eseguito il programma e ha avuto l'output:
terminare chiamato senza un'eccezione attiva
Abortito (core scaricato)
È possibile avere un output errato come questo, con l'output corretto di "sì. Così sia!", Inserito all'interno. Tuttavia, tutto ciò è ancora inaccettabile.
Il problema con questo output errato è che i thread non sono stati uniti. E così, i thread operavano in modo indipendente, portando alla confusione. La soluzione è quella di unirsi a Thr1 al thread principale e poiché thr1 chiama Thr2, proprio come il filo principale chiama Thr1, Thr2 deve essere unita a Thr1. Questo è illustrato nella prossima sezione.
Unirsi a un thread
La sintassi per unire un thread al thread chiamante è:
Discussione.giuntura();
dove join () è una funzione membro di un oggetto thread. Questa espressione deve essere all'interno del corpo del thread chiamante. Questa espressione deve essere all'interno del corpo della funzione chiamante, che è un thread efficace.
Il seguente programma è il programma sopra ripetuto, ma con il corpo del filo principale che si unisce a Thr1 e il corpo di Thr1 che si unisce a Thr2:
#includere
#includere
#includere
Utilizzo dello spazio dei nomi std;
String Globl1 = String ("So");
String Globl2 = String ("Sia!");
void fn2 (string ST2)
String Globl = Globl1 + ST2;
cout << globl << endl;
void fn1 (string ST1)
Globl1 = "Sì. " + ST1;
Discussione Thr2 (Fn2, Globl2);
thr2.giuntura();
int main ()
Discussione Thr1 (Fn1, Globl1);
Thr1.giuntura();
/ * Alcune affermazioni */
restituzione 0;
Nota le posizioni di attesa, in cui le dichiarazioni di join sono state inserite nel programma. L'output è:
"SÌ. Così sia!"
Senza le citazioni, come previsto, pulito e chiaro, inequivocabile ". Thr2 non ha bisogno di alcuna dichiarazione di join nel suo corpo; non chiama alcun thread.
Quindi, il corpo del thread chiamante si unisce al thread chiamato.
Future :: get ()
La libreria standard C ++ ha un sub-biblioteca chiamato Future. Questo sub-biblioteca ha una classe chiamata Future. La libreria ha anche una funzione chiamata Async (). La classe, Future, ha una funzione membro chiamata get (). Oltre al suo ruolo principale, questa funzione ha l'effetto di unire due funzioni che sono in esecuzione contemporaneamente o in parallelo. Le funzioni non devono essere thread.
La funzione asincrone ()
Si noti che i thread soprattutto restituiscono vuoto. Un thread è una funzione che è sotto controllo. Alcune funzioni non restituiscono vuoto ma restituiscono qualcosa. Quindi, alcuni thread restituiscono qualcosa.
La funzione Async () può assumere una funzione di alto livello come argomento ed eseguire la funzione contemporaneamente o in parallelo alla funzione chiamante. In questo caso, non ci sono thread, solo una funzione di chiamata e una funzione chiamata argomentazione per la funzione asincrona (). Una semplice sintassi per la funzione Async è:
Future Futobj = Async (fn, fnargs)
La funzione Async restituisce un oggetto futuro. Il primo argomento qui, per la funzione Async, è il nome della funzione di alto livello. Ci può essere più di un argomento dopo questo. Il resto degli argomenti sono argomenti della funzione di alto livello.
Se la funzione di alto livello restituisce un valore, quel valore sarà un membro dell'oggetto futuro. E questo è un modo per imitare un thread che restituisce un valore.
Future :: get ()
La funzione Async restituisce un oggetto futuro. Questo oggetto futuro ha il valore di ritorno della funzione che è un argomento per la funzione asincrimale. Per ottenere questo valore, è necessario utilizzare la funzione membro get () del futuro. Lo scenario è:
Future Futobj = Async (fn, fnargs);
Digita futobj.Ottenere();
Quando viene chiamata la funzione get (), il corpo della funzione chiamante attende (blocchi), fino a quando la funzione asincrimale non ha restituito il suo valore. Successivamente, il resto delle dichiarazioni sotto l'istruzione get () continua a eseguire.
Il seguente programma è quello sopra, in cui non è stato creato alcun thread ufficialmente e, al posto della dichiarazione di join, è stata utilizzata la dichiarazione get (). La funzione Async () è stata utilizzata per simulare un thread. Le due funzioni di alto livello sono state ridotte a una. Il programma è:
#includere
#includere
#includere
Utilizzo dello spazio dei nomi std;
String Globl1 = String ("So");
String Globl2 = String ("Sia!");
String fn (string ST1, string ST2)
String Concat = ST1 + ST2;
restituire concat;
int main ()
Future FUT = Async (Fn, Globl1, Globl2);
string ret = fut.Ottenere(); // main () aspetta qui
String Result = "Sì. " + ret;
cout << result << endl;
restituzione 0;
Si noti che è stata inclusa la libreria futura, anziché la biblioteca di thread. L'output è:
SÌ. Così sia!
Conclusione
Quando è preoccupata una dichiarazione di join, sono coinvolte due funzioni di alto livello. Uno è la funzione di chiamata e l'altro è la funzione chiamata. Nel corpo della funzione chiamante è l'istruzione join. Queste due funzioni possono essere incapsulate in un thread. La funzione membro join () del thread chiamato è nel corpo del thread chiamante. Quando viene chiamata la funzione join (), il thread chiamante attende a quel punto (blocchi) fino al completamento del thread chiamato; prima che continui a funzionare.
L'uso di thread può essere evitato utilizzando la funzione asincrona () nella biblioteca futura. Questa funzione prende una funzione di alto livello come argomento e restituisce un oggetto futuro, che contiene il valore restituito della funzione dell'argomento alla funzione Async (). Al fine di ottenere il valore di ritorno della funzione come argomento, la funzione membro get () dell'oggetto futuro deve essere utilizzata. Quando viene chiamata la funzione membro get (), il corpo della funzione di chiamata attende a quel punto (blocchi) fino a quando la funzione chiamata non completa; prima che continui a funzionare.