J'ai essayé de comprendre les subtilités de l'interaction des threads POSIX et des signaux POSIX. En particulier, je suis intéressé par:
Pour savoir pourquoi je veux cela, je recherche comment convertir le package TclX pour prendre en charge les threads, ou le diviser et au moins faire certaines parties utiles pour prendre en charge les threads. Les signaux sont l'une de ces parties qui présente un intérêt particulier.
- Quelle est la meilleure façon de contrôler à quel thread un signal est délivré?
Comme l'a indiqué @ zoli2k, nommer explicitement un seul thread pour gérer tous les signaux que vous souhaitez gérer (ou un ensemble de threads ayant chacun des responsabilités de signal spécifiques), est une bonne technique.
- Quelle est la meilleure façon de dire à un autre thread (qui pourrait en fait être occupé) que le signal est arrivé? [...]
- Comment puis-je gérer en toute sécurité la transmission des informations qu'un signal s'est produit à d'autres threads? Cela doit-il se produire dans le gestionnaire de signaux?
Je ne dirai pas "mieux", mais voici ma recommandation:
Bloquez tous les signaux souhaités dans main
, afin que tous les threads héritent de ce masque de signal. Ensuite, modélisez le thread de réception de signal spécial comme une boucle d'événement pilotée par signal, distribuant les signaux nouvellement arrivés sous la forme d'une autre communication intra-thread .
La façon la plus simple de le faire est de demander au thread d'accepter les signaux dans une boucle en utilisant sigwaitinfo
ou sigtimedwait
. Le thread convertit ensuite les signaux d'une manière ou d'une autre, diffusant peut-être un pthread_cond_t
, Réveillant d'autres threads avec plus d'E/S, mettant en file d'attente une commande dans une file d'attente thread-safe spécifique à l'application, peu importe.
Alternativement, le fil spécial pourrait permettre de transmettre des signaux à un gestionnaire de signaux, en ne les démasquant que lorsqu'ils sont prêts à traiter les signaux. (Cependant, la transmission du signal via les gestionnaires a tendance à être plus sujette aux erreurs que l'acceptation du signal via la famille sigwait
.) Dans ce cas, le gestionnaire de signal du récepteur effectue une action simple et sûre pour le signal asynchrone: définir sig_atomic_t
Drapeaux, appelant sigaddset(&signals_i_have_seen_recently, latest_sig)
, write
() un octet à un non-bloquant self-pipe , etc. Ensuite, de retour dans sa principale masquée boucle, le thread communique la réception du signal aux autres threads comme ci-dessus.
( [~ # ~] mise à jour [~ # ~] @caf souligne à juste titre que les approches sigwait
sont supérieures.)
Selon la norme POSIX, tous les threads doivent apparaître avec le même PID sur le système et en utilisant pthread_sigmask()
vous pouvez définir le masque de blocage du signal pour chaque thread.
Puisqu'il est autorisé de définir un seul gestionnaire de signal par PID, je préfère gérer tous les signaux dans un thread et envoyer pthread_cancel()
si un thread en cours doit être annulé. C'est le moyen préféré contre pthread_kill()
car il permet de définir des fonctions de nettoyage pour les threads.
Sur certains systèmes plus anciens, en raison du manque de prise en charge appropriée du noyau, les threads en cours d'exécution peuvent avoir un PID différent du PID du thread parent. Voir FAQ pour la gestion du signal avec linuxThreads sur Linux 2.4 .
À mon humble avis, les signaux Unix V et les threads posix ne se mélangent pas bien. Unix V est 1970. POSIX est 1980;)
Il y a des points d'annulation et si vous autorisez les signaux et les pthreads dans une seule application, vous finirez par écrire des boucles autour de chaque appel, ce qui peut étonnamment retourner EINTR.
Donc, ce que j'ai fait dans les (quelques) cas où j'ai dû programmer du multithread sur Linux ou QNX était de masquer tous les signaux pour tous les threads (sauf un).
Lorsqu'un signal Unix V arrive, le processus bascule la pile (c'était autant de simultanéité dans Unix V que vous pourriez obtenir dans un processus).
Comme le suggèrent les autres articles ici, il pourrait être possible maintenant, de dire au système, quel thread posix sera victime de ce changement de pile.
Une fois que vous avez réussi à faire fonctionner votre thread de gestionnaire de signal, la question demeure, comment transformer les informations de signal en quelque chose de civilisé, d'autres threads peuvent utiliser. Une infrastructure pour les communications inter-threads est requise. Un modèle, utile, est le modèle d'acteur, où chacun de vos threads est la cible d'un mécanisme de messagerie en cours.
Donc, au lieu d'annuler d'autres threads ou de les tuer (ou d'autres choses étranges), vous devriez essayer de rassembler le signal du contexte Signal vers votre thread de gestion du signal, puis utiliser vos mécanismes de communication de modèle d'acteur pour envoyer des messages sémantiquement utiles à ces acteurs, qui ont besoin des informations relatives au signal.
Où j'en suis jusqu'à présent:
Je dois encore trier signal
vs sigaction
, pselect
, sigwait
, sigaltstack
, et tout un tas d'autres bits et des morceaux d'API POSIX (et non-POSIX).