Crea un pool di thread in C ++

Crea un pool di thread in C ++
Un pool di thread è un insieme di thread in cui ogni thread ha una sorta di compito da svolgere. Così diversi thread svolgono diversi tipi di attività. Quindi ogni thread ha la sua specializzazione di compiti. Un'attività è fondamentalmente una funzione. Funzioni simili sono fatte da un thread particolare; Un diverso set di funzioni simili viene svolto da un altro thread e così via. Sebbene un thread in esecuzione esegui una funzione di livello superiore, un thread per definizione è l'istanza di un oggetto dalla classe thread. Diversi thread hanno argomenti diversi, quindi un thread particolare dovrebbe occuparsi di un insieme simile di funzioni.

In C ++, questo pool di thread deve essere gestito. C ++ non ha una libreria per la creazione di un pool di thread ed è gestione. Questo probabilmente perché ci sono diversi modi per creare un pool di thread. Quindi, un programmatore C ++ deve creare un pool di thread in base alle esigenze.

Cos'è un thread? Un thread è un oggetto istanziato dalla classe thread. Nella normale istanza, il primo argomento del costruttore di thread è il nome di una funzione di alto livello. Il resto degli argomenti al costruttore di thread sono argomenti per la funzione. Man mano che il thread viene istanziato, la funzione inizia a eseguire. La funzione C ++ main () è una funzione di alto livello. Altre funzioni in quell'ambito globale sono funzioni di alto livello. Succede che la funzione principale () sia un thread che non ha bisogno di una dichiarazione formale come fanno altri thread. Considera il seguente programma:

#includere
#includere
Utilizzo dello spazio dei nomi std;
void func ()
cout << "code for first output" << endl;
cout << "code for second output" << endl;

int main ()

Thread Thr (func);
thr.giuntura();
/ * altre dichiarazioni */
restituzione 0;

L'output è:

Codice per il primo output
Codice per il secondo output

Nota l'inclusione della libreria thread che ha la classe thread. func () è una funzione di alto livello. La prima istruzione nella funzione principale () la utilizza nell'istanziazione del thread, thr. La prossima dichiarazione in main (), è una dichiarazione di join. Si unisce al filo thr al corpo del thread di funzione principale (), nella posizione è codificato. Se questa istruzione è assente, la funzione principale potrebbe essere eseguita al completamento senza il completamento della funzione thread. Ciò significa guai.

Un comando simile a quello seguente dovrebbe essere utilizzato per eseguire un programma C ++ 20 di thread, per il compilatore G ++:

g ++ -std = c ++ 2a temp.cpp -lpthread -o temp

Questo articolo spiega un modo per creare e gestire un pool di thread in C++.

Contenuto dell'articolo

  • Requisiti di esempio del pool di thread
  • Variabili globali
  • La funzione di filo principale
  • funzione principale
  • Conclusione

Requisiti di esempio del pool di thread

I requisiti per questo pool di thread illustrativo sono semplici: ci sono tre thread e un thread principale. I thread sono subordinati al filo principale. Ogni thread subordinato funziona con una struttura di dati in coda. Quindi ci sono tre code: Qu1, Qu2 e Qu3. La libreria delle code, così come la libreria di thread, devono essere incluse nel programma.

Ogni coda può avere più di una chiamata di funzione ma della stessa funzione di alto livello. Cioè, ogni elemento di una coda è per una funzione di funzione di una particolare funzione di alto livello. Quindi, ci sono tre diverse funzioni di livello superiore: una funzione di livello superiore per thread. I nomi delle funzioni sono fn1, fn2 e fn3.

La funzione richiede ogni coda differisce solo nei loro argomenti. Per semplicità e per questo esempio, le chiamate di funzione non avranno alcun argomento. In effetti, il valore di ciascuna coda in questo esempio sarà lo stesso numero intero: 1 del valore per tutti gli elementi QU1; 2 come valore per tutti gli elementi QU2; e 3 come valore per tutti gli elementi QU3.

Una coda è una struttura First_in-First_out. Quindi la prima chiamata (numero) per inserire una coda è la prima a partire. Quando una chiamata (numero) lascia, la funzione corrispondente e il suo thread vengono eseguiti.

La funzione principale () è responsabile dell'alimentazione di ciascuna delle tre code, con le richieste per le funzioni appropriate, quindi i thread appropriati.

Il thread principale è responsabile del controllo se c'è una chiamata in qualsiasi coda e, se c'è una chiamata, chiama la funzione appropriata tramite il suo thread. In questo programma Esempio, quando nessuna coda ha alcun thread, il programma termina.

Le funzioni di alto livello sono semplici, per questo esempio pedagogico, sono:

void fn1 ()
cout << "fn1" << endl;

void fn2 ()
cout << "fn2" << endl;

void fn3 ()
cout << "fn3" << endl;

I fili corrispondenti saranno Thr1, Thr2 e Thr3. Il thread principale ha la sua funzione principale. Qui, ogni funzione ha una sola affermazione. L'output della funzione fn1 () è "fn1". L'output della funzione fn2 () è "fn2". L'output della funzione fn3 () è "fn3".

Alla fine di questo articolo, il lettore può mettere insieme tutti i segmenti di codice in questo articolo per formare un programma di pool di thread.

Variabili globali

La parte superiore del programma con le variabili globali, è:

#includere
#includere
#includere
Utilizzo dello spazio dei nomi std;
coda qu1;
coda qu2;
coda qu3;
Discussione Thr1;
Discussione Thr2;
Discussione Thr3;

La coda e le variabili del thread sono variabili globali. Sono stati dichiarati senza inizializzazione o dichiarazione. Dopo questo, nel programma, dovrebbero essere le tre funzioni di livello superiore subordinato, come mostrato sopra.

La libreria iostream è inclusa per l'oggetto cout. La libreria di thread è inclusa per i thread. I nomi dei thread sono thr1, thr2 e thr3. La libreria di code è inclusa per le code. I nomi delle code sono Qu1, Qu2 e Qu3. Qu1 corrisponde a Thr1; Qu2 corrisponde a Thr2 e Qu3 corrisponde a Thr3. Una coda è come un vettore, ma è per FIFO (First_in-First_out).

La funzione di filo principale

Dopo le tre funzioni subordinate di livello superiore sono la funzione principale nel programma. È:

void masterfn ()
lavoro:
if (Qu1.size ()> 0) thr1 = thread (fn1);
if (Qu2.size ()> 0) thr2 = thread (fn2);
if (qu3.size ()> 0) thr3 = thread (fn3);
if (Qu1.size ()> 0)
Qu1.pop();
Thr1.giuntura();

if (Qu2.size ()> 0)
Qu2.pop();
thr2.giuntura();

if (qu3.size ()> 0)
Qu3.pop();
thr3.giuntura();

if (Qu1.size () == 0 && qu1.size () == 0 && qu1.size () == 0)
ritorno;
vai a lavorare;

Il goto-loop incarna tutto il codice della funzione. Quando tutte le code sono vuote, la funzione restituisce nullo, con l'affermazione, "return;".

Il primo segmento di codice nel goto-loop ha tre istruzioni: una per ogni coda e il thread corrispondente. Qui, se una coda non è vuota, viene eseguita il suo thread (e la corrispondente funzione di livello superiore subordinato).

Il segmento di codice successivo è costituito da tre costruzioni if, ciascuno corrispondente a un thread subordinato. Ogni If-costruct ha due dichiarazioni. La prima dichiarazione rimuove il numero (per la chiamata), che potrebbe aver avuto luogo nel primo segmento di codice. Il prossimo è un'istruzione join, che assicura che il thread corrispondente funzioni fino al completamento.

L'ultima affermazione nel goto-loop termina la funzione, uscendo dal ciclo se tutte le code sono vuote.

Funzione principale

Dopo la funzione di thread principale nel programma, dovrebbe essere la funzione principale (), il cui contenuto è:

Qu1.Push (1);
Qu1.Push (1);
Qu1.Push (1);
Qu2.Push (2);
Qu2.Push (2);
Qu3.Push (3);
Thread Masterthr (Masterfn);
cout << "Program has started:" << endl;
Masterthr.giuntura();
cout << "Program has ended." << endl;

La funzione principale () è responsabile del inserire i numeri che rappresentano le chiamate nelle code. Qu1 ha tre valori di 1; Qu2 ha due valori di 2 e Qu3 ha un valore di 3. La funzione principale () inizia il filo principale e si unisce al suo corpo. Un output del computer dell'autore è:

Il programma è iniziato:
fn2
fn3
fn1
fn1
fn2
fn1
Il programma è terminato.

L'output mostra le operazioni simultanee irregolari dei thread. Prima che la funzione principale () si unisca al suo thread principale, visualizza "il programma è iniziato:". Il thread principale chiama thr1 per fn1 (), thr2 per fn2 () e thr3 per fn3 (), in quell'ordine. Tuttavia, l'uscita corrispondente inizia con "Fn2", quindi "FN3" e quindi "FN1". Non c'è niente di sbagliato in questo ordine iniziale. Ecco come funziona la concorrenza, in modo irregolare. Il resto delle stringhe di uscita appaiono come le loro funzioni venivano chiamate.

Dopo che il corpo della funzione principale si è unito al thread principale, ha aspettato che il thread principale fosse completato. Per il completamento del thread principale, tutte le code devono essere vuote. Ogni valore della coda corrisponde all'esecuzione del thread corrispondente. Quindi, affinché ogni coda diventasse vuota, il suo thread deve eseguire per quel numero di volte; Ci sono elementi in coda.

Quando il thread principale e i suoi thread sono stati eseguiti e terminati, la funzione principale continua a eseguire. E mostra: “Il programma è finito.".

Conclusione

Un pool di thread è un set di thread. Ogni thread è responsabile dello svolgimento delle proprie attività. Le attività sono funzioni. In teoria, i compiti arrivano sempre. Non finiscono davvero, come illustrato nell'esempio sopra. In alcuni esempi pratici, i dati sono condivisi tra i thread. Per condividere i dati, il programmatore ha bisogno di conoscenza di condizional_variabile, funzione asincrona, promessa e futuro. Questa è una discussione per un'altra volta.