Question

La documentation de Twisted m'a amené à croire qu'il était acceptable de combiner des techniques telles que reactor.spawnProcess() et threads.deferToThread() dans la même application, que le réacteur gérerait cela avec élégance sous les couvertures.En l'essayant, j'ai constaté que mon application se bloquait.En utilisant plusieurs threads par eux-mêmes ou des processus enfants par eux-mêmes, tout va bien.

En regardant la source du réacteur, je trouve que le SelectReactor.spawnProcess() la méthode appelle simplement os.fork() sans aucune considération pour plusieurs threads qui pourraient être en cours d'exécution.Ceci explique les blocages, car à partir de l'appel à os.fork() vous aurez deux processus avec plusieurs threads simultanés en cours d'exécution et faisant qui sait quoi avec les mêmes descripteurs de fichiers.

Ma question pour SO est la suivante : quelle est la meilleure stratégie pour résoudre ce problème ?

Ce que j'ai en tête, c'est de sous-classer SelectReactor, pour que ce soit un singleton et appelle os.fork() une seule fois, immédiatement une fois instancié.Le processus enfant s'exécutera en arrière-plan et agira en tant que serveur pour le parent (en utilisant la sérialisation des objets sur des canaux pour communiquer dans les deux sens).Le parent continue d'exécuter l'application et peut utiliser les threads à sa guise.Appels à spawnProcess() dans le processus parent sera délégué au processus enfant, qui sera assuré de n'avoir qu'un seul thread en cours d'exécution et pourra donc appeler os.fork() sans encombre.

Quelqu'un a-t-il déjà fait cela ?Y at-il un moyen plus rapide?

Était-ce utile?

La solution 3

En revenant sur ce problème après un certain temps, j'ai découvert que si je fais ceci :

reactor.callFromThread(reactor.spawnProcess, *spawnargs)

au lieu de cela:

reactor.spawnProcess(*spawnargs)

alors le problème disparaît dans mon petit cas de test.Il y a une remarque dans la documentation Twisted "Using Processes" qui m'a amené à essayer ceci :"La plupart du code dans Twisted n'est pas thread-safe.Par exemple, l'écriture de données dans un transport à partir d'un protocole n'est pas thread-safe."

Je soupçonne que les autres personnes mentionnées par Jean-Paul qui avaient ce problème commettent peut-être une erreur similaire.Il incombe à l'application de veiller à ce que le réacteur et les autres appels d'API soient effectués dans le bon thread.Et apparemment, à quelques exceptions près, le « fil correct » est presque toujours le fil principal du réacteur.

Autres conseils

Quelle est la meilleure stratégie pour résoudre ce problème ?

Déposer un ticket (peut-être après enregistrement) décrivant le problème, de préférence avec un scénario de test reproductible (pour une précision maximale).Ensuite, il peut y avoir une discussion sur la meilleure façon (ou les meilleures façons - différentes plates-formes peuvent exiger une solution différente) de la mettre en œuvre.

L'idée de créer immédiatement un processus enfant pour faciliter la création ultérieure de processus enfants a déjà été évoquée, pour résoudre le problème de performances lié à la récolte des processus enfants.Si cette approche résout désormais deux problèmes, elle commence à paraître un peu plus attrayante.Une difficulté potentielle avec cette approche est que spawnProcess renvoie de manière synchrone un objet qui fournit le PID de l'enfant et permet de lui envoyer des signaux.C'est un peu plus de travail à mettre en œuvre s'il y a un processus intermédiaire sur le chemin, puisque le PID devra être communiqué au processus principal avant spawnProcess Retour.Un défi similaire consistera à soutenir le childFDs argument, puisqu'il ne sera plus possible d'hériter simplement des descripteurs de fichiers dans le processus enfant.

Une solution alternative (qui peut être un peu plus compliquée, mais qui peut aussi présenter moins de problèmes de mise en œuvre) pourrait consister à appeler sys.setcheckinterval avec un très grand numéro avant d'appeler os.fork, puis restaurez l'intervalle de vérification d'origine dans le processus parent uniquement.Cela devrait suffire pour éviter tout changement de thread dans le processus jusqu'à ce que le os.execvpe a lieu, détruisant tous les threads supplémentaires.Ce n'est pas tout à fait correct, car cela laissera certaines ressources (telles que les mutex et les conditions) dans un mauvais état, mais vous les utilisez avec deferToThread ce n'est pas très courant, donc cela n'affecte peut-être pas votre cas.

Le conseil que donne Jean-Paul dans sa réponse est bon, mais cela devrait fonctionne (et le fait dans la plupart des cas).

Premièrement, Twisted utilise également des threads pour la résolution du nom d'hôte, et j'ai certainement utilisé des sous-processus dans les processus Twisted qui établissent également des connexions client.Cela peut donc fonctionner en pratique.

Deuxième, fork() ne crée pas plusieurs threads dans le processus enfant. Selon la norme décrivant fork(),

Un processus doit être créé avec un seul thread.Si un processus multithread appelle fork(), le nouveau processus doit contenir une réplique du thread appelant...

Maintenant, cela ne veut pas dire qu'il y a Non problèmes potentiels de multithreading avec spawnProcess;la norme dit également :

...pour éviter les erreurs, le processus enfant ne peut exécuter des opérations sécurisées que par un signal asynchrone jusqu'à ce que l'une des fonctions d'exécution soit appelée...

et je ne pense pas qu'il y ait quoi que ce soit pour garantir que seules des opérations sécurisées avec un signal asynchrone soient utilisées.

Veuillez donc être plus précis quant à votre problème exact, car il ne s'agit pas d'un sous-processus avec des threads clonés.

fork() sous Linux laisse définitivement le processus enfant avec un seul thread.

Je suppose que vous savez que, lorsque vous utilisez des threads dans Twisted, la SEULE API Twisted que les threads sont autorisés à appeler est callFromThread ?Toutes les autres API Twisted doivent uniquement être appelées à partir du thread principal du réacteur.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top