Je recommanderais également d'utiliser une sélection ou un autre moyen non basé sur le signal pour terminer votre fil. L'une des raisons pour lesquelles nous avons des discussions est d'essayer de nous éloigner de la folie des signaux. Cela dit...
Généralement on utilise pthread_kill() avec SIGUSR1 ou SIGUSR2 pour envoyer un signal au thread. Les autres signaux suggérés - SIGTERM, SIGINT, SIGKILL - ont une sémantique à l'échelle du processus qui ne vous intéresse peut-être pas.
Quant au comportement lorsque vous avez envoyé le signal, je suppose que cela a à voir avec la façon dont vous avez géré le signal. Si vous n'avez pas de gestionnaire installé, l'action par défaut de ce signal est appliquée, mais dans le contexte du thread qui a reçu le signal. Ainsi, SIGALRM, par exemple, serait "géré" par votre thread, mais la gestion consisterait à terminer le processus - probablement pas le comportement souhaité.
La réception d'un signal par le thread le fera généralement sortir d'une lecture avec EINTR, à moins qu'il ne soit vraiment dans cet état ininterruptible comme mentionné dans une réponse précédente. Mais je pense que non, sinon vos expériences avec SIGALRM et SIGIO n'auraient pas mis fin au processus.
Votre lecture est-elle peut-être dans une sorte de boucle ? Si la lecture se termine par -1 return, alors sortez de cette boucle et quittez le thread.
Vous pouvez jouer avec ce code très bâclé que j'ai mis en place pour tester mes hypothèses - je suis à quelques fuseaux horaires de mes livres POSIX en ce moment...
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <signal.h>
int global_gotsig = 0;
void *gotsig(int sig, siginfo_t *info, void *ucontext)
{
global_gotsig++;
return NULL;
}
void *reader(void *arg)
{
char buf[32];
int i;
int hdlsig = (int)arg;
struct sigaction sa;
sa.sa_handler = NULL;
sa.sa_sigaction = gotsig;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
if (sigaction(hdlsig, &sa, NULL) < 0) {
perror("sigaction");
return (void *)-1;
}
i = read(fileno(stdin), buf, 32);
if (i < 0) {
perror("read");
} else {
printf("Read %d bytes\n", i);
}
return (void *)i;
}
main(int argc, char **argv)
{
pthread_t tid1;
void *ret;
int i;
int sig = SIGUSR1;
if (argc == 2) sig = atoi(argv[1]);
printf("Using sig %d\n", sig);
if (pthread_create(&tid1, NULL, reader, (void *)sig)) {
perror("pthread_create");
exit(1);
}
sleep(5);
printf("killing thread\n");
pthread_kill(tid1, sig);
i = pthread_join(tid1, &ret);
if (i < 0)
perror("pthread_join");
else
printf("thread returned %ld\n", (long)ret);
printf("Got sig? %d\n", global_gotsig);
}
Vieille question qui pourrait très bien recevoir une nouvelle réponse car les choses ont évolué et une nouvelle technologie est maintenant disponible pour mieux gérer les signaux dans les threads.
Depuis le noyau Linux 2.6.22, le système propose une nouvelle fonction appelée signalfd()
qui peut être utilisé pour ouvrir un descripteur de fichier pour un ensemble donné de signaux Unix (en dehors de ceux qui tuent carrément un processus.)
// defined a set of signals
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
// ... you can add more than one ...
// prevent the default signal behavior (very important)
sigprocmask(SIG_BLOCK, &set, nullptr);
// open a file descriptor using that set of Unix signals
f_socket = signalfd(-1, &set, SFD_NONBLOCK | SFD_CLOEXEC);
Vous pouvez maintenant utiliser le poll()
ou select()
fonctions pour écouter le signal avec le descripteur de fichier plus habituel (socket, fichier sur disque, etc.) que vous écoutiez.
Le NONBLOCK est important si vous voulez une boucle qui peut vérifier les signaux et autres descripteurs de fichier encore et encore (c'est-à-dire qu'il est également important sur votre autre descripteur de fichier).
J'ai une telle implémentation qui fonctionne avec (1) des minuteries, (2) des sockets, (3) des pipes, (4) des signaux Unix, (5) des fichiers normaux. En fait, vraiment n'importe quel descripteur de fichier plus les minuteries.
https://github.com/m2osw/snapcpp/blob/master/snapwebsites/libsnapwebsites/src/snapwebsites/snap_communicator.cpp
https://github.com/m2osw/snapcpp/blob/master/snapwebsites/libsnapwebsites/src/snapwebsites/snap_communicator.h
Vous pouvez également être intéressé par des bibliothèques telles que libevent
La manière canonique de le faire est avec pthread_cancel
, où le thread a fait pthread_cleanup_push
/pop
pour fournir un nettoyage pour toutes les ressources qu'il utilise.
Malheureusement, cela ne peut PAS être utilisé dans le code C++, jamais. N'importe quel code de bibliothèque standard C++ ou N'IMPORTE QUEL try {} catch()
sur la pile appelante au moment de pthread_cancel
segvi tuera potentiellement tout votre processus.
La seule solution consiste à gérer SIGUSR1
, définition d'un drapeau d'arrêt, pthread_kill(SIGUSR1)
, alors partout où le thread est bloqué sur les E/S, si vous obtenez EINTR
vérifiez l'indicateur d'arrêt avant de réessayer l'E/S. En pratique, cela ne réussit pas toujours sous Linux, je ne sais pas pourquoi.
Mais dans tous les cas, il est inutile d'en parler si vous devez appeler une bibliothèque tierce, car ils auront très probablement une boucle serrée qui redémarre simplement les E/S sur EINTR
. L'ingénierie inverse de leur descripteur de fichier pour le fermer ne suffira pas non plus - ils pourraient attendre un sémaphore ou une autre ressource. Dans ce cas, il est tout simplement impossible d'écrire du code fonctionnel, point final. Oui, c'est complètement endommagé au cerveau. Parlez aux gars qui ont conçu les exceptions C++ et pthread_cancel
. Soi-disant, cela pourrait être corrigé dans une future version de C++. Bonne chance avec ça.
Votre select()
pourrait avoir un délai d'attente, même s'il est peu fréquent, afin de quitter le thread avec élégance sous certaines conditions. Je sais, les sondages, c'est nul...
Une autre alternative consiste à avoir un tube pour chaque enfant et à l'ajouter à la liste des descripteurs de fichiers surveillés par le thread. Envoyez un octet au tube depuis le parent lorsque vous souhaitez que cet enfant sorte. Pas d'interrogation au prix d'un tuyau par thread.