Multithreading C ++

Le multithreading est une forme spécialisée de multitâche et un multitâche est la fonctionnalité qui permet à votre ordinateur d'exécuter simultanément deux programmes ou plus. En général, il existe deux types de multitâche: basé sur les processus et basé sur les threads.

Le multitâche basé sur les processus gère l'exécution simultanée des programmes. Le multitâche basé sur les threads traite de l'exécution simultanée d'éléments du même programme.

Un programme multithread contient deux ou plusieurs parties qui peuvent s'exécuter simultanément. Chaque partie d'un tel programme est appelée un thread, et chaque thread définit un chemin d'exécution distinct.

C ++ ne contient aucune prise en charge intégrée pour les applications multithread. Au lieu de cela, il repose entièrement sur le système d'exploitation pour fournir cette fonctionnalité.

Ce tutoriel suppose que vous travaillez sur Linux OS et que nous allons écrire un programme C ++ multi-thread en utilisant POSIX. POSIX Threads, ou Pthreads fournit des API qui sont disponibles sur de nombreux systèmes POSIX de type Unix tels que FreeBSD, NetBSD, GNU / Linux, Mac OS X et Solaris.

Créer des threads

La routine suivante est utilisée pour créer un thread POSIX -

#include <pthread.h>
pthread_create (thread, attr, start_routine, arg)

Ici, pthread_createcrée un nouveau thread et le rend exécutable. Cette routine peut être appelée un certain nombre de fois depuis n'importe où dans votre code. Voici la description des paramètres -

Sr.Non Paramètre et description
1

thread

Identificateur unique et opaque du nouveau thread renvoyé par le sous-programme.

2

attr

Un objet attribut opaque qui peut être utilisé pour définir des attributs de thread. Vous pouvez spécifier un objet d'attributs de thread ou NULL pour les valeurs par défaut.

3

start_routine

La routine C ++ que le thread exécutera une fois qu'il est créé.

4

arg

Un seul argument qui peut être passé à start_routine. Il doit être passé par référence comme un cast de pointeur de type void. NULL peut être utilisé si aucun argument ne doit être passé.

Le nombre maximum de threads pouvant être créés par un processus dépend de l'implémentation. Une fois créés, les threads sont pairs et peuvent créer d'autres threads. Il n'y a pas de hiérarchie ou de dépendance implicite entre les threads.

Terminer les threads

Il y a la routine suivante que nous utilisons pour terminer un thread POSIX -

#include <pthread.h>
pthread_exit (status)

Ici pthread_exitest utilisé pour quitter explicitement un thread. En règle générale, la routine pthread_exit () est appelée après qu'un thread a terminé son travail et n'est plus nécessaire d'exister.

Si main () se termine avant les threads qu'il a créés et quitte avec pthread_exit (), les autres threads continueront à s'exécuter. Sinon, ils seront automatiquement arrêtés lorsque main () se termine.

Example

Cet exemple de code simple crée 5 threads avec la routine pthread_create (). Chaque fil imprime un "Hello World!" message, puis se termine par un appel à 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);
}

Compilez le programme suivant en utilisant la bibliothèque -lpthread comme suit -

$gcc test.cpp -lpthread

Maintenant, exécutez votre programme qui donne la sortie suivante -

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

Passer des arguments aux threads

Cet exemple montre comment passer plusieurs arguments via une structure. Vous pouvez transmettre n'importe quel type de données dans un rappel de thread car il pointe vers void, comme expliqué dans l'exemple suivant -

#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);
}

Lorsque le code ci-dessus est compilé et exécuté, il produit le résultat suivant -

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

Rejoindre et détacher des threads

Il existe deux routines suivantes que nous pouvons utiliser pour joindre ou détacher des threads -

pthread_join (threadid, status) 
pthread_detach (threadid)

Le sous-programme pthread_join () bloque le thread appelant jusqu'à ce que le thread spécifié 'threadid' se termine. Lorsqu'un thread est créé, l'un de ses attributs définit s'il peut être joint ou détaché. Seuls les threads créés comme joignables peuvent être joints. Si un thread est créé comme détaché, il ne peut jamais être joint.

Cet exemple montre comment attendre la fin des threads à l'aide de la routine de jointure 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);
}

Lorsque le code ci-dessus est compilé et exécuté, il produit le résultat suivant -

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.