Multithreading C ++
Il multithreading è una forma specializzata di multitasking e il multitasking è la funzionalità che consente al computer di eseguire due o più programmi contemporaneamente. In generale, ci sono due tipi di multitasking: basato su processo e basato su thread.
Il multitasking basato sui processi gestisce l'esecuzione simultanea dei programmi. Il multitasking basato su thread si occupa dell'esecuzione simultanea di parti dello stesso programma.
Un programma multithread contiene due o più parti che possono essere eseguite contemporaneamente. Ciascuna parte di tale programma è chiamata thread e ogni thread definisce un percorso di esecuzione separato.
C ++ non contiene alcun supporto integrato per le applicazioni multithread. Invece, si basa interamente sul sistema operativo per fornire questa funzionalità.
Questo tutorial presuppone che tu stia lavorando su un sistema operativo Linux e che scriveremo un programma C ++ multi-thread utilizzando POSIX. POSIX Threads, o Pthreads fornisce API che sono disponibili su molti sistemi POSIX simili a Unix come FreeBSD, NetBSD, GNU / Linux, Mac OS X e Solaris.
Creazione di thread
La seguente routine viene utilizzata per creare un thread POSIX:
#include <pthread.h>
pthread_create (thread, attr, start_routine, arg)
Qui, pthread_createcrea un nuovo thread e lo rende eseguibile. Questa routine può essere chiamata un numero qualsiasi di volte da qualsiasi punto del codice. Ecco la descrizione dei parametri:
Suor n | Parametro e descrizione |
---|---|
1 | thread Un identificatore univoco opaco per il nuovo thread restituito dalla subroutine. |
2 | attr Un oggetto attributo opaco che può essere utilizzato per impostare gli attributi del thread. È possibile specificare un oggetto attributi thread o NULL per i valori predefiniti. |
3 | start_routine La routine C ++ che il thread eseguirà una volta creato. |
4 | arg Un singolo argomento che può essere passato a start_routine. Deve essere passato per riferimento come cast di puntatore di tipo void. NULL può essere utilizzato se non deve essere passato alcun argomento. |
Il numero massimo di thread che possono essere creati da un processo dipende dall'implementazione. Una volta creati, i thread sono pari e possono creare altri thread. Non esiste alcuna gerarchia o dipendenza implicita tra i thread.
Terminazione dei thread
C'è la seguente routine che usiamo per terminare un thread POSIX -
#include <pthread.h>
pthread_exit (status)
Qui pthread_exitviene utilizzato per uscire esplicitamente da un thread. Tipicamente, la routine pthread_exit () viene chiamata dopo che un thread ha completato il suo lavoro e non è più necessario che esista.
Se main () termina prima dei thread che ha creato ed esce con pthread_exit (), gli altri thread continueranno ad essere eseguiti. Altrimenti, verranno automaticamente terminati al termine di main ().
Example
Questo semplice codice di esempio crea 5 thread con la routine pthread_create (). Ogni thread stampa un "Hello World!" messaggio, quindi termina con una chiamata a pthread_exit ().
#include <iostream>
#include <cstdlib>
#include <pthread.h>
using namespace std;
#define NUM_THREADS 5
void *PrintHello(void *threadid) {
long tid;
tid = (long)threadid;
cout << "Hello World! Thread ID, " << tid << endl;
pthread_exit(NULL);
}
int main () {
pthread_t threads[NUM_THREADS];
int rc;
int i;
for( i = 0; i < NUM_THREADS; i++ ) {
cout << "main() : creating thread, " << i << endl;
rc = pthread_create(&threads[i], NULL, PrintHello, (void *)i);
if (rc) {
cout << "Error:unable to create thread," << rc << endl;
exit(-1);
}
}
pthread_exit(NULL);
}
Compilare il seguente programma utilizzando la libreria -lpthread come segue:
$gcc test.cpp -lpthread
Ora, esegui il tuo programma che dà il seguente output:
main() : creating thread, 0
main() : creating thread, 1
main() : creating thread, 2
main() : creating thread, 3
main() : creating thread, 4
Hello World! Thread ID, 0
Hello World! Thread ID, 1
Hello World! Thread ID, 2
Hello World! Thread ID, 3
Hello World! Thread ID, 4
Passaggio di argomenti ai thread
Questo esempio mostra come passare più argomenti tramite una struttura. Puoi passare qualsiasi tipo di dati in un thread callback perché punta a void come spiegato nell'esempio seguente:
#include <iostream>
#include <cstdlib>
#include <pthread.h>
using namespace std;
#define NUM_THREADS 5
struct thread_data {
int thread_id;
char *message;
};
void *PrintHello(void *threadarg) {
struct thread_data *my_data;
my_data = (struct thread_data *) threadarg;
cout << "Thread ID : " << my_data->thread_id ;
cout << " Message : " << my_data->message << endl;
pthread_exit(NULL);
}
int main () {
pthread_t threads[NUM_THREADS];
struct thread_data td[NUM_THREADS];
int rc;
int i;
for( i = 0; i < NUM_THREADS; i++ ) {
cout <<"main() : creating thread, " << i << endl;
td[i].thread_id = i;
td[i].message = "This is message";
rc = pthread_create(&threads[i], NULL, PrintHello, (void *)&td[i]);
if (rc) {
cout << "Error:unable to create thread," << rc << endl;
exit(-1);
}
}
pthread_exit(NULL);
}
Quando il codice precedente viene compilato ed eseguito, produce il seguente risultato:
main() : creating thread, 0
main() : creating thread, 1
main() : creating thread, 2
main() : creating thread, 3
main() : creating thread, 4
Thread ID : 3 Message : This is message
Thread ID : 2 Message : This is message
Thread ID : 0 Message : This is message
Thread ID : 1 Message : This is message
Thread ID : 4 Message : This is message
Unire e staccare i fili
Ci sono le seguenti due routine che possiamo usare per unire o staccare i thread:
pthread_join (threadid, status)
pthread_detach (threadid)
La subroutine pthread_join () blocca il thread chiamante finché il thread 'threadid' specificato non termina. Quando viene creato un thread, uno dei suoi attributi definisce se è unibile o scollegato. È possibile unire solo i thread creati come unibili. Se un thread viene creato come scollegato, non potrà mai essere unito.
Questo esempio dimostra come attendere il completamento del thread utilizzando la routine di join Pthread.
#include <iostream>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>
using namespace std;
#define NUM_THREADS 5
void *wait(void *t) {
int i;
long tid;
tid = (long)t;
sleep(1);
cout << "Sleeping in thread " << endl;
cout << "Thread with id : " << tid << " ...exiting " << endl;
pthread_exit(NULL);
}
int main () {
int rc;
int i;
pthread_t threads[NUM_THREADS];
pthread_attr_t attr;
void *status;
// Initialize and set thread joinable
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
for( i = 0; i < NUM_THREADS; i++ ) {
cout << "main() : creating thread, " << i << endl;
rc = pthread_create(&threads[i], &attr, wait, (void *)i );
if (rc) {
cout << "Error:unable to create thread," << rc << endl;
exit(-1);
}
}
// free attribute and wait for the other threads
pthread_attr_destroy(&attr);
for( i = 0; i < NUM_THREADS; i++ ) {
rc = pthread_join(threads[i], &status);
if (rc) {
cout << "Error:unable to join," << rc << endl;
exit(-1);
}
cout << "Main: completed thread id :" << i ;
cout << " exiting with status :" << status << endl;
}
cout << "Main: program exiting." << endl;
pthread_exit(NULL);
}
Quando il codice precedente viene compilato ed eseguito, produce il seguente risultato:
main() : creating thread, 0
main() : creating thread, 1
main() : creating thread, 2
main() : creating thread, 3
main() : creating thread, 4
Sleeping in thread
Thread with id : 0 .... exiting
Sleeping in thread
Thread with id : 1 .... exiting
Sleeping in thread
Thread with id : 2 .... exiting
Sleeping in thread
Thread with id : 3 .... exiting
Sleeping in thread
Thread with id : 4 .... exiting
Main: completed thread id :0 exiting with status :0
Main: completed thread id :1 exiting with status :0
Main: completed thread id :2 exiting with status :0
Main: completed thread id :3 exiting with status :0
Main: completed thread id :4 exiting with status :0
Main: program exiting.