Fabrication industrielle
Internet des objets industriel | Matériaux industriels | Entretien et réparation d'équipement | Programmation industrielle |
home  MfgRobots >> Fabrication industrielle >  >> Industrial programming >> Langue C

Maîtriser le multithreading en C++ : Programmation concurrente efficace avec POSIX

Le multithreading est une forme spécialisée de multitâche. Le multitâche permet à un ordinateur d'exécuter plusieurs programmes simultanément. On distingue deux types :

Un programme multithreadé contient au moins deux parties s'exécutant simultanément. Chaque partie est un thread, définissant un chemin d'exécution distinct.

C++ ne possède pas de support natif pour le multithreading avant C++11 ; il s'appuie sur l'API du système d'exploitation. Ce tutoriel suppose un environnement Linux et utilise POSIX Threads (Pthreads), disponible sur les systèmes Unix-like (FreeBSD, NetBSD, GNU/Linux, macOS, Solaris).

Création de threads

La routine pthread_create crée un nouveau thread POSIX :

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine)(void *), void *arg);
ParamètreDescription
threadIdentifiant unique du thread créé (sortie).
attrAttributs du thread (ou NULL pour les valeurs par défaut).
start_routineFonction que le thread exécutera.
argArgument unique passé à la fonction (pointeur void*).

Le nombre maximal de threads est dépendant de l'implémentation. Une fois créés, les threads sont pairs et peuvent en créer d'autres ; il n'y a pas de hiérarchie implicite.

Terminaison des threads

La fonction pthread_exit termine explicitement un thread :

#include <pthread.h>
void pthread_exit(void *status);

Si main() se termine avant ses threads et appelle pthread_exit(), les autres threads continuent. Sinon, ils sont arrêtés automatiquement à la fin de main().

Exemple : 5 threads « Hello World »

#include <iostream>
#include <cstdlib>
#include <pthread.h>

using namespace std;
#define NUM_THREADS 5

void *PrintHello(void *threadid) {
    long tid = (long)threadid;
    cout << "Hello World! Thread ID, " << tid << endl;
    pthread_exit(NULL);
}

int main() {
    pthread_t threads[NUM_THREADS];
    int rc, i;

    for (i = 0; i < NUM_THREADS; i++) {
        cout << "main() : création du thread, " << i << endl;
        rc = pthread_create(&threads[i], NULL, PrintHello, (void *)i);
        if (rc) {
            cout << "Erreur : impossible de créer le thread, " << rc << endl;
            exit(-1);
        }
    }
    pthread_exit(NULL);
}

Compilation : g++ test.cpp -lpthread

Sortie typique (l'ordre peut varier) :

main() : création du thread, 0
main() : création du thread, 1
main() : création du thread, 2
main() : création du thread, 3
main() : création du 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

Passage d'arguments aux threads

On utilise souvent une structure pour passer plusieurs arguments :

#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 = (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, i;

    for (i = 0; i < NUM_THREADS; i++) {
        cout << "main() : création du thread, " << i << endl;
        td[i].thread_id = i;
        td[i].message = "Ceci est un message";
        rc = pthread_create(&threads[i], NULL, PrintHello, (void *)&td[i]);
        if (rc) {
            cout << "Erreur : impossible de créer le thread, " << rc << endl;
            exit(-1);
        }
    }
    pthread_exit(NULL);
}

Joindre et détacher les threads

Deux routines permettent de synchroniser les threads :

pthread_join(threadid, status)
pthread_detach(threadid)

Exemple : attendre la fin des threads avec pthread_join

#include <iostream>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>

using namespace std;
#define NUM_THREADS 5

void *wait(void *t) {
    long tid = (long)t;
    sleep(1);
    cout << "Thread en sommeil " << endl;
    cout << "Thread avec id : " << tid << " ...sortie " << endl;
    pthread_exit(NULL);
}

int main() {
    pthread_t threads[NUM_THREADS];
    pthread_attr_t attr;
    void *status;
    int rc, i;

    // Initialiser les attributs et rendre les threads joinable
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

    for (i = 0; i < NUM_THREADS; i++) {
        cout << "main() : création du thread, " << i << endl;
        rc = pthread_create(&threads[i], &attr, wait, (void *)i);
        if (rc) {
            cout << "Erreur : impossible de créer le thread, " << rc << endl;
            exit(-1);
        }
    }

    // Libérer les attributs et attendre les threads
    pthread_attr_destroy(&attr);
    for (i = 0; i < NUM_THREADS; i++) {
        rc = pthread_join(threads[i], &status);
        if (rc) {
            cout << "Erreur : impossible de joindre, " << rc << endl;
            exit(-1);
        }
        cout << "Main: thread terminé id :" << i;
        cout << " sortie avec statut :" << status << endl;
    }
    cout << "Main: fin du programme." << endl;
    pthread_exit(NULL);
}

Sortie :

main() : création du thread, 0
main() : création du thread, 1
main() : création du thread, 2
main() : création du thread, 3
main() : création du thread, 4
Thread en sommeil
Thread avec id : 0 ....sortie
Thread avec id : 1 ....sortie
Thread avec id : 2 ....sortie
Thread avec id : 3 ....sortie
Thread avec id : 4 ....sortie
Main: thread terminé id :0 sortie avec statut :0
Main: thread terminé id :1 sortie avec statut :0
Main: thread terminé id :2 sortie avec statut :0
Main: thread terminé id :3 sortie avec statut :0
Main: thread terminé id :4 sortie avec statut :0
Main: fin du programme.

Ce tutoriel couvre les bases du multithreading POSIX en C++. Pour les projets modernes C++ (C++11 et ultérieur), privilégiez std::thread, std::mutex, std::condition_variable et les algorithmes parallèles de la bibliothèque standard.

Langue C

  1. Maîtriser les opérateurs C++ : Guide complet avec exemples pratiques
  2. Commentaires C++ : Bonnes pratiques pour un code lisible et maintenable
  3. Maîtriser les templates de classe en C++ : Guide pratique
  4. Langage C++ : Vue d'ensemble et caractéristiques clés
  5. Constantes et littéraux en C++ : Types, syntaxe et bonnes pratiques
  6. Guide complet des opérateurs C++ : Types, utilisation et bonnes pratiques
  7. Maîtriser les types numériques en C++ : Guide pratique
  8. Comprendre les références en C++ : Concepts clés et différences avec les pointeurs
  9. Maîtriser les templates C++ : Guide de la programmation générique