Comment créer un thread Linux en C

Comment créer un thread Linux en C

Sous Linux, vous pouvez créer et gérer des threads en C/C++ à l’aide de la bibliothèque de threads POSIX (pthread). Contrairement à d’autres systèmes d’exploitation, il y a peu de différence entre un thread et un processus sous Linux. C’est pourquoi Linux fait souvent référence à ses threads comme des processus légers.

En utilisant la bibliothèque pthread, vous pouvez créer des threads, attendre qu’ils se terminent et les terminer explicitement.

L’historique de l’utilisation des threads sous Linux

Avant la version 2.6 de Linux, l’implémentation principale du thread était LinuxThreads. Cette implémentation présentait des limites importantes en termes de performances et d’opérations de synchronisation. Une limite sur le nombre maximum de threads pouvant s’exécuter les a limités à des milliers.

En 2003, une équipe dirigée par des développeurs d’IBM et de RedHat a réussi à rendre disponible le projet Native POSIX Thread Library (NPTL). Il a été introduit pour la première fois dans RedHat Enterprise version 3 pour résoudre les problèmes de performances avec la machine virtuelle Java sous Linux. Aujourd’hui, la bibliothèque GNU C contient des implémentations des deux mécanismes de threading.

Aucun de ceux-ci n’est une implémentation de threads verts, qu’une machine virtuelle gérerait et exécuterait en mode purement utilisateur. Lorsque vous utilisez la bibliothèque pthread, le noyau crée un thread à chaque démarrage d’un programme.

Vous pouvez trouver des informations spécifiques aux threads pour tout processus en cours d’exécution dans les fichiers sous /proc/<PID>/task . Il s’agit de l’emplacement standard pour les informations de processus sous la norme procfs Linux. Pour les applications à thread unique, il apparaîtra qu’il existe un enregistrement de tâche avec la même valeur que le PID sous ce répertoire.

Logique de travail des threads

Les threads sont comme des processus en cours d’exécution sur le système d’exploitation. Dans les systèmes à processeur unique (par exemple, les microcontrôleurs), le noyau du système d’exploitation simule les threads. Cela permet aux transactions de s’exécuter simultanément via le découpage.

Un système d’exploitation monocœur ne peut réellement exécuter qu’un seul processus à la fois. Cependant, dans les systèmes multicœurs ou multiprocesseurs, ces processus peuvent s’exécuter simultanément.

Création de threads en C

Vous pouvez utiliser la fonction pthread_create pour créer un nouveau thread. Le fichier d’en-tête pthread.h inclut sa définition de signature ainsi que d’autres fonctions liées aux threads. Les threads utilisent le même espace d’adressage et les mêmes descripteurs de fichiers que le programme principal.

La bibliothèque pthread inclut également le support nécessaire pour les opérations mutex et conditionnelles requises pour les opérations de synchronisation.

Lorsque vous utilisez les fonctions de la bibliothèque pthread, vous devez vous assurer que le compilateur lie la bibliothèque pthread à votre exécutable. Si nécessaire, vous pouvez demander au compilateur de créer un lien vers la bibliothèque à l’aide de l’ option -l :

gcc -o test test_thread.c -lpthread

La fonction pthread_create a la signature suivante :

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)

Elle renvoie 0 si la procédure a réussi. En cas de problème, il renvoie un code d’erreur non nul. Dans la signature de fonction ci-dessus :

  • Le paramètre de thread est de type pthread_t . Le fil créé sera toujours accessible avec cette référence.
  • Le paramètre attr vous permet de spécifier un comportement personnalisé. Vous pouvez utiliser une série de fonctions spécifiques aux threads commençant par pthread_attr_ pour configurer cette valeur. Les personnalisations possibles sont la stratégie de planification, la taille de la pile et la stratégie de détachement.
  • start_routine spécifie la fonction que le thread exécutera.
  • arg représente une structure de données générique transmise à la fonction par le thread.

Voici un exemple d’application :

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

void *worker(void *data)
{
char *name = (char*)data;

for (int i = 0; i < 120; i++)
{
usleep(50000);
printf("Hi from thread name = %s\n", name);
}

printf("Thread %s done!\n", name);
return NULL;
}

int main(void)
{
pthread_t th1, th2;
pthread_create(&th1, NULL, worker, "X");
pthread_create(&th2, NULL, worker, "Y");
sleep(5);
printf("Exiting from main program\n");
return 0;
}

Sortie du programme montrant deux threads exécutés simultanément

Types de filetage

Lorsqu’un thread revient de la fonction main() dans une application, tous les threads se terminent et le système libère toutes les ressources utilisées par le programme. De même, lorsque vous quittez un thread avec une commande comme exit() , votre programme terminera tous les threads.

Avec la fonction pthread_join , vous pouvez attendre qu’un thread se termine à la place. Le thread utilisant cette fonction se bloquera jusqu’à ce que le thread attendu se termine. Les ressources qu’ils utilisent à partir du système ne sont pas renvoyées même dans des cas tels que l’arrêt de threads joignables, non planifié par le processeur ou même l’échec de la jointure avec ptread_join .

Parfois, il y a des situations où joindre avec pthread_join n’a pas de sens ; s’il est impossible de prédire quand le fil se terminera, par exemple. Dans ce cas, vous pouvez vous assurer que le système renvoie automatiquement toutes les ressources au point où le thread revient.

Pour ce faire, vous devez démarrer les discussions pertinentes avec le statut DÉTACHÉ . Lors du démarrage d’un thread, le statut DETACH peut être défini via les valeurs d’un attribut de thread ou avec la fonction pthread_detach :

int pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
int pthread_detach(pthread_t thread);

Voici un exemple d’utilisation de pthread_join(). Remplacez la fonction principale du premier programme par la suivante :

int main(void)
{
pthread_t th1, th2;
pthread_create(&th1, NULL, worker, "X");
pthread_create(&th2, NULL, worker, "Y");
sleep(5);
printf("exiting from main program\n");
pthread_join(th1, NULL);
pthread_join(th2, NULL);
return 0;
}

Lorsque vous compilez et exécutez le programme, votre sortie sera :

Hi from thread Y
Hi from thread X
Hi from thread Y
...
Hi from thread Y
exiting from main program
Hi from thread X
...
Hi from thread X
Thread X done!
Hi from thread Y
Thread Y done!

Terminaison de fil

Vous pouvez annuler un thread avec un appel à pthread_cancel, en passant l’ identifiant pthread_t correspondant :

int pthread_cancel(pthread_t thread);

Vous pouvez voir cela en action dans le code suivant. Encore une fois, seule la fonction principale est différente :

int main(void)
{
pthread_t th1, th2;
pthread_create(&th1, NULL, worker, "X");
pthread_create(&th2, NULL, worker, "Y");
sleep(1);
printf("====> Cancelling Thread Y!!\n");
pthread_cancel(th2);
usleep(100000);
printf("====> Cancelling Thread X!\n");
pthread_cancel(th1);
printf("exiting from main program\n");
return 0;
}

Sortie d'un programme montrant les threads en cours d'exécution puis en cours d'annulation

Pourquoi les fils de discussion sont-ils créés ?

Les systèmes d’exploitation essaient toujours d’exécuter des threads sur un ou plusieurs processeurs, soit à partir d’une liste créée par l’utilisateur, soit à partir d’une liste de threads créée par l’utilisateur. Certains threads ne peuvent pas s’exécuter car ils attendent un signal d’entrée/sortie du matériel. Ils peuvent également attendre volontairement, attendre une réponse d’un autre thread ou être bloqués par un autre thread.

Vous pouvez ajuster les ressources que vous allouez aux threads que vous créez à l’aide de pthread. Il peut s’agir d’une politique de planification personnalisée ou vous pouvez choisir des algorithmes de planification tels que FIFO ou Round-robin si vous le souhaitez.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *