J'écris un serveur de socket multithreaded et j'ai besoin de savoir avec certitude. P>
Les articles sur les threads disent que j'aurais dû attendre que le fil revienne, au lieu de le tuer. Dans certains cas, cependant, le fil de l'utilisateur que je veux frapper / interdiction, ne pourra pas revenir correctement (par exemple, j'ai commencé à envoyer un gros bloc de données et d'envoyer () bloque le fil en ce moment), donc je vais besoin juste de le tuer. p>
Pourquoi tuer des fonctions de fil est dangereux et quand peuvent-ils planter toute la demande? P>
6 Réponses :
Si vous tuez votre fil de manière difficile, il peut fuir des ressources. P>
Vous pouvez l'éviter lorsque vous concevez votre fil pour supporter l'annulation. P>
N'utilisez pas de blocage d'appels ni d'utiliser des appels de blocage avec un délai d'attente. Recevoir ou envoyer des données en petits morceaux ou de manière asynchrone. P>
Mais comment faire du temps sur les prises de blocage (gagnant)?
Utilisez des prises non bloquantes. Ou définir un délai d'attente qui correspond à vos besoins.
N'oubliez pas que vous pouvez envoyer de petits morceaux.
De plus, si vous utilisez un blocage d'E / S sur des sockets, la fermeture de la prise d'un autre contexte de thread annulera les E / S bloqués (sous Windows, de toute façon) afin que le fil bloqué reprend le contrôle et peut se terminer correctement.
Il y a beaucoup de raisons, mais voici une solution facile: il n'y a qu'un seul tas. Si un thread alloue quelque chose sur le tas, et que vous le tuez, tout ce qu'il a été alloué est là jusqu'à ce que le processus se termine. Chaque fil obtient sa propre pile, et cela peut être libéré (dépendant de la mise en œuvre), mais vous garantissez des fuites sur le tas en ne le laissant pas fermer. P>
Strictement parler, vous peut i> la mémoire de libération allouée par un autre fil tant que la mémoire est toujours en cours de référence (c'est-à-dire une structure de données partagée à thread). Cela n'empêche pas le fil d'être interrompu entre l'allocation et le stockage du pointeur, cependant.
Assez réellement André, mais bien sûr, je faisais appelé tout ce que le fil alloué et n'est pas partagé de quelque manière que ce soit entre les threads. Mais à proprement parler, vous êtes correct, en ce que si elle "partagée" le pointeur, il pourrait être désactivé, mais même il est peu probable qu'il soit désaffecté, car comment le "2e fil" sache que le "1er thread" "(Celui que nous avons tué) ne l'a-t-il pas encore? Vous auriez besoin de niveaux supplémentaires de programmation «intéressante» se produisant pour détecter cela.
Je pensais à une utilisation plus simple, telle que le maintien d'une liste de (code alloué de manière dynamique) des instances code> qui ont une référence à leur propre thread. Le fil principal pourrait vérifier périodiquement chaque client et voir si le thread est terminé (ou terminé) et désaffecter la liste à cette époque. Je ne proposais pas cela comme une solution viable (voir ma réponse), mais plus comme un "informatique techniquement est i> faisable" type de commentaire ...
Tuer un thread peut causer des ressources de votre programme car le fil n'a pas eu la chance de nettoyer après elle-même. Considérez la fermeture de la poignée du socket Le fil envoie. Cela provoquera l'envoi de blocage () de retourner immédiatement avec un code d'erreur approprié. Le fil peut ensuite nettoyer et mourir paisiblement. P>
Cela semble tellement bon et facile, mais ne fera pas l'accident du programme?
Ça va causer le fil avec la prise maintenant fermée pour échouer son envoi, ce qui est exactement le point
@Glorian: Si votre code ne vérifie pas le code de retour code> () code>, vous pourriez avoir un comportement assez imprévisible. Cependant, fermer la prise lors d'un appel à envoyer () code> ne placera pas votre application.
Solution lisse, n'avait pas eu de cela.
Dans le cas d'un fil bloqué dans des E / S, vous n'avez jamais vraiment besoin de le tuer, vous avez le choix entre des E / S non bloquantes, des délais d'attente et une fermeture de la prise d'un autre fil. L'un de ceux-ci débloquera le fil. P>
Bien que IIRC sur Linux (par opposition à la plupart des autres unes), la fermeture de la prise d'un autre thread ne débloque pas le fil, qui est très malheureux.
@Ringding: même après des jours? Si oui, je pense que vous devriez signaler cela en tant que bogue dans bugzilla.kernel.org
@March: Je vais enquêter sur ceci. Mais avoir à attendre des heures ou des jours pourraient ne pas être aussi utiles dans de nombreuses situations.
@Ringding: Il y a une grande différence entre les heures d'attente par rapport à toujours. Le premier peut généralement être reconfiguré facilement pendant que ce dernier ne peut pas.
@March: Parlez-vous d'envoyer () en particulier ou tout appels de blocage sur les sockets? Parce que je suis un programme de test (à partir d'ici: << a href = "http://bugs.python.org/issue8831>" rel = "nfollow noreferrer"> bugs.python.org/issue8831> ), et jusqu'à présent, il n'est pas retourné, après 36 heures. Il se termine immédiatement sur Solaris.
Le fait qu'au moins certains appels de système de blocage ne reviennent pas lorsque vous fermez la prise de la prise sous eux se reflète également dans la JVM OpenJDK, où nous pouvons voir qu'un signal est envoyé uniquement sur Linux dans le seul but d'interrompre le système de blocage. Call:
tuer un fil signifie arrêter toute exécution exactement où il s'agit du moment. En particulier, il n'exécutera aucun destructeurs. Cela signifie que les sockets et les fichiers ne seront pas fermés, la mémoire allouée de manière dynamique ne sera pas libérée, les mutiles et les sémaphores ne seront pas libérés, etc. Tuer un thread est presque garanti de causer des fuites de ressources et des blocages. P>
Ainsi, votre question est un peu inversée. La vraie question devrait lire: p>
Quand, et sous quelles conditions peuvent em> je tue un fil? p> blockQuote>
Donc, vous pouvez tuer le fil lorsque vous êtes convaincu aucune fuite et des blocages ne peuvent pas se produire, et non plus, et pas lorsque le code de l'autre thread sera modifié (il est donc presque impossible de garantir). P >
Dans votre cas spécifique, la solution consiste à utiliser des prises non bloquantes et à vérifier un drapeau de thread / spécifique à l'utilisateur entre les appels vers
envoyer () code> et
recv () code> . Cela compliquera probablement votre code, ce qui est probablement pourquoi vous avez résisté à le faire, mais c'est la bonne façon d'y aller. P>
En outre, vous vous rendrez rapidement compte qu'une approche thread-per-client ne s'élève pas. Vous allez donc changer votre architecture et réécrire de tout de temps en temps. P>
À propos du seul bon moment pour tuer un fil est si vous le faites sur le point de fermer tout le processus.
+1: Dans Windows, nous finissons tous avec des ports d'achèvement de l'IO éventuellement.
@JOHN: Malheureusement, les ports d'achèvement des E / S ne sont pas pour les faibles de cœur. Je ne le recommanderais pas à quelqu'un à moins d'avoir une expérience juste d'expérience avec les autres conceptions de Socket Server. Un design avec une seule échelle de fil liée à des E / S remarquable remarquablement bien (bien que cela ne vous donnerait pas un serveur C10K) et est beaucoup plus facile à saisir, en particulier pour les étudiants.
@Andre: accepté. Mais je sais aussi que des commentaires comme "dans Windows, nous finissons tous avec les ports d'achèvement de l'IO," étaient éventuellement les types de choses qui m'ont amené à découvrir de nouvelles techniques dans de nombreux cas.
L'une des principales raisons pour lesquelles Java 1.4 a obtenu Java.nio. * C'était de se débarrasser de l'approche à une seule bloquée-thread-Per-Socket, et donc de l'ampleur beaucoup mieux. Même si vous n'utilisez pas Java, il est instructif de regarder comment cette API est conçue.
@John: Ces commentaires m'aident aussi beaucoup, c'est pourquoi je préfère votre commentaire!
Vous ne voulez vraiment pas faire cela. P>
Si vous tuez un fil de fil pendant qu'il contient une section critique, il ne sera pas publié, ce qui entraînera probablement une nouvelle application. Certains appels de bibliothèque C comme la répartition de la mémoire de la mémoire Heap utilisent des sections critiques et si vous tuez votre thread pendant que cela fait un "nouveau" puis appeler nouveau de n'importe où ailleurs dans votre programme entraînera ce thread. P>
vous simplement ne peux pas em> le faire en toute sécurité sans des mesures vraiment extrêmes qui sont beaucoup plus restrictives que simplement signaler le fil pour se terminer. P>
Vous voudrez peut-être mentionner le système d'exploitation ...
Oh, désolé, j'ai oublié. Windows XP SP3
Ne tuez jamais de fil, car vous allez tuyer votre application.
Parce que c'est contre le 6ème commandement.
@Noah "Tu ne tueras pas ... ou votre risque éternel
vide code> et tu as ther goto enfer code>."