Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 11/03/2015, à 14:25

totola

python: arrêter un thread

Bonjour,
À chaque fois que j'avance un nouveau problème apparaît.. smile
Après avoir résolu le nombre max de session ssh; je bloque au problème du trop grand nombre de threads lancés (une centaine)... Je pensais bêtement que la commande "mon_thread._Thread__stop()" suffisait pour les arrêter, mais apparemment ce n'est pas le cas... à partir d'une centaine lancé j'ai l'erreur suivante:

  File "/usr/lib/python2.7/threading.py", line 494, in start     _start_new_thread(self.__bootstrap, ()) error: can't start new thread

voilà une partie de mon code incriminé:

		def parler_tous(poste):
			def callback():
				connecter_ssh(poste)
				with settings(warn_only=True):
					run("cvlc {} & echo $! > PIDpt.txt)
			self.tPt = threading.Thread(target=callback)
			self.tPt.start()

		def effacer_tous(poste):
			connecter_ssh(poste)
			with settings(warn_only=True):
				run("kill $(cat PIDpt.txt)")
			self.tPt._Thread__stop()

Et comme je lance ça sur 10 postes en même temps... Au bout de la dixième fois ça coince...

J'ai vu ça sur Internet:

To fix it you will need increase stack size limit in the shell:

# ulimit -s unlimited

Initially it was:

$ ulimit -s
8192

déjà ça ne marche pas (le problème est identique) et en plus l'idée ne me plaît qu'à moitié... J'aimerais bien pouvoir arrêter mes threads plutôt que d'en créer à l'infini...

Pourquoi "self.tPt._Thread__stop()" ne marche pas?

Merci!
smile

Hors ligne

#2 Le 25/04/2015, à 10:58

grim7reaper

Re : python: arrêter un thread

Salut,

Arrêter un thread n’est pas quelque chose d’anodin (il peut-être au milieu d’une opération critique), c’est pour cela qu’il n’y a pas de façon simple et sûre dans 100% des cas. Le mieux étant que le thread se termine de lui-même.
Sinon, je n’ai pas l’impression que les threads soit la meilleure solution à ton problème.

Qu’essaye tu de faire exactement ?

Hors ligne

#3 Le 25/04/2015, à 11:10

totola

Re : python: arrêter un thread

Salut et merci de la réponse !
En fait je fais une sorte de 'labo de langue' pour des profs d'anglais, cad que le prof puisse écouter et parler a des postes qu'il choisit...
Tout ça fonctionne avec avconv et cvlc sur des IP multi ast.
Chaque action (parler a un poste, écouter un autre, mettre en communication 2 postes élèves...etc) necessitent un petit thread qui contient au max 2 commande avconv (pour diffuser l'audio d'un poste sur le réseau) et une cvlc (pour écouter l'audio diffusé). Quand le prof arrête d'écouter, je 'tue' le processus VLC en ayant préalablement récupéré son pid...
Ça marche bien mais je n'arrête pas le thread (qui reste 'vide') et au bout d'un moment ça coince...
Notamment si j'utilise ma fonction 'parler a tous' qui créé une dizaine de threads a la fois ( au bout du dixième appel il coince ' too many thread'... Ou QQ chose comme ça..)
Merci de t'intéresser a mon problème... J'avoue qu'après avoir bien avancé j'ai un peu baissé les bras juste a cause de ça!
wink

Hors ligne

#4 Le 25/04/2015, à 19:55

grim7reaper

Re : python: arrêter un thread

Ok.

Tu as vraiment besoin d’avoir un thread qui tourne en permanence ?
Parce que si tu veux seulement lancer VLC, tu peux le faire dans un processus à part (je te conseille de jeter un coup d’œil au module subprocess (qui contient la classe Popen), ça devrait t’intéresser).

Si tu dois vraiment passer par des threads, il faudrait s'assurer que la fonction qui est lancé par ton thread se termine d’elle-même à un moment. Selon la doc’ ça devrait résoudre ton problème :

https://docs.python.org/3/library/threading.html#thread-objects a écrit :

Once a thread object is created, its activity must be started by calling the thread’s start() method. This invokes the run() method in a separate thread of control.

Once the thread’s activity is started, the thread is considered ‘alive’. It stops being alive when its run() method terminates – either normally, or by raising an unhandled exception. The is_alive() method tests whether the thread is alive.

Hors ligne

#5 Le 27/04/2015, à 10:23

totola

Re : python: arrêter un thread

Salut et merci pour la réponse!
En effet, ça peut m'intéresser... Je regarde ça.

En attendant je détaille un peu plus mon exemple de thread que je lance:

### exemple pour écouter un poste élève depuis le poste prof:
def callback1E():
	os.system('cvlc {} & echo $! > PID1e.txt &'.format(self.IP_multicast[1]))    #lance cvlc (et récupère son PID) sur le poste prof en écoute sur l'IP
	connecter_ssh(1)                                                                  #se connecte sur le poste via ssh (module fabric de python)
	with settings(warn_only=True):
		run("avconv -f alsa -i hw:0,0 -acodec mp2 -b 128k -f rtp {}".format(self.IP_multicast[1]))   #envoie le signal du micro sur l'IP
self.t11 = threading.Thread(target=callback1E)
self.t11.start()

ça c'est pour écouter, pour parler j'inverse cvlc et avconv...
Mais du coup je pourrais utiliser subprocess dans ce cas-là??

Dernière modification par totola (Le 27/04/2015, à 10:39)

Hors ligne

#6 Le 27/04/2015, à 13:18

grim7reaper

Re : python: arrêter un thread

Salut !

D’où vient la fonction run que tu appelles dans callback1E ?
Normalement, si cette fonction run se termine (je ne connais pas cette fonction mais je suppose qu’elle va se terminer quand avconv est stoppé ou tué) alors callback1E va se terminer et ton thread devrait disparaître.
Si avconv n’est jamais arrêté, alors je suppose que la fonction run ne se termine pas non plus (et donc ton thread va continuer d’exister).
En plus, comme le thread est occupé en dehors de l’interpréteur Python (il exécute une commande externe) ça pourrait expliquer que tu ne puisses pas le stopper même via _Thread__stop).

totola a écrit :

Mais du coup je pourrais utiliser subprocess dans ce cas-là??

Pour la commande en locale (cvlc dans ton exemple) oui. En plus, en passant par Popen tu pourrais même faire un kill du process sans avoir à passer par un fichier PID externe
Pour la commande via SSH, ça devrait être possible (à vérifier).

Hors ligne

#7 Le 27/04/2015, à 15:19

totola

Re : python: arrêter un thread

Salut,
la fonction run() vient du module fabric:

www.fabfile.org

elle lance via ssh la commande contenue à l'intérieur... une sorte de os.system() sur un poste distant.
en fait les commandes avconv ne se terminent pas en principe (seulement à la fermeture du programme), j'avais même pensé les lancer toutes en même temps à l'ouverture du programme car j'avais peur que le programme plante lorsqu'on relance plusieurs fois la même commande avconv (pour écouter un poste puis un autre...). En réalité si avconv est déjà lancé (en fait si hw:0,0 est déjà occupé), avconv renvoie juste une erreur qui n'est pas bloquante. De cette façon lorsqu'on lance la commande avconv, si ce n'est pas déjà fait, elle démarre l'envoie du signal micro sur l'IP multicast sinon elle renvoie une erreur et passe à la suite... ce qui me convient car cela laisse la possibilité d'allumer ou de redémarrer un poste sans être obligé de relancer le programme.

Avec tout ce que tu me dis là je crois comprendre ce qui cloche, j'ai en réalité un autre problème avec le PID que j'ai contourné de manière pas tout à fait académique... Je m'explique. Pour une raison que j'ignore, la commande suivante qui est dans mon thread:

run("cvlc IP & echo $! > PID5p.txt")

ne fonctionne pas, impossible de récupérer le PID de quelque manière que ce soit, j'ai l'impression que le programme bloque là-dessus et n'exécute plus rien ensuite dans le thread... Alors que si je place une boucle infinie derrière, ça marche bien:

run("cvlc IP & echo $! > PID5p.txt | while (true) do sleep 500; done")

du coup, ça doit être pour ça que les threads ne s'arrêtent pas... Il faudrait que j'arrive à régler proprement ce problème et peut-être que ça me réglerait le reste... non??

À vrai dire j'ai un fil ouvert là-dessus et on m'a indiqué cette page, mais je n'ai pas tout compris..

http://www.fabfile.org/faq.html#why-can … abric-hang

plus de détail ici:
https://www.debian-fr.org/recuperer-pid … 51159.html

Hors ligne

#8 Le 27/04/2015, à 20:24

grim7reaper

Re : python: arrêter un thread

Ok, merci pour les détails.

Je pense que le problème vient de la méthode run qui « bloque » le thread.
Pour vérifier cette hypothèse, tu pourrais ajouter un print après l’appel à run et voir si quelque chose s’affiche.

Hors ligne

#9 Le 27/04/2015, à 23:52

totola

Re : python: arrêter un thread

Alors, après essais, si ma commande run se termine, pas de soucis je peux bien afficher qq chose avec un print... Par contre si je laisse la boucle infinie dans le run, rien ne se passe ensuite (logique).

Par contre, il y a autre chose que je ne comprends absolument pas... C'est cette histoire de PID:

Par exemple, si j'execute:

def callback1():
	connecter_ssh(1)
	run("cvlc {} & echo $! > PID1.txt | while (true) do sleep 500; done".format(self.IP_multicast[0]))
self.t = threading.Thread(target=callback1)
self.t.start()

un cat PID1.txt me donne 12253

un ps -aux me donne bien un processus vlc avec le bon pid:

lavaren+ 12246  0.0  0.0  16652  1464 pts/26   Ss+  23:40   0:00 /bin/bash -l -c cvlc 
lavaren+ 12253  0.5  0.3 440656 13092 pts/26   Sl+  23:40   0:00 /usr/bin/vlc -I dummy
lavaren+ 12255  0.0  0.0  16652   608 pts/26   S+   23:40   0:00 /bin/bash -l -c cvlc 
lavaren+ 12257  0.0  0.0  11424   612 pts/26   S+   23:40   0:00 sleep 500

puis je le tue:

	connecter_ssh(1)
	run("kill $(cat PID1.txt)")

il disparaît bien:

lavaren+ 12246  0.0  0.0  16652  1464 pts/26   Ss+  23:40   0:00 /bin/bash -l -c cvlc 
lavaren+ 12255  0.0  0.0  16652   608 pts/26   S+   23:40   0:00 /bin/bash -l -c cvlc 
lavaren+ 12257  0.0  0.0  11424   612 pts/26   S+   23:40   0:00 sleep 500

au passage je ne sais pas ce que sont les /bin/bash -l -c cvlc qui restent..?? Et le sleep 500 c'est ma boucle infinie qui fait que le thread perdure...

Alors, oui, vous me direz pourquoi donc mettre ce sleep 500 en boucle... C'est vrai que c'est plutôt tordu comme idée... Soit. Enlevons-le:

def callback1():
	connecter_ssh(1)
	run("cvlc {} & echo $! > PID1.txt".format(self.IP_multicast[0]))
self.t = threading.Thread(target=callback1)
self.t.start()

un cat PID1.txt donne cette fois-ci 13358

mais pas de trace de vlc :

root     13310  0.3  0.1 124368  4484 ?        Ss   23:47   0:00 sshd: lavarenne [priv
lavaren+ 13351  0.0  0.0 124368  2116 ?        S    23:47   0:00 sshd: lavarenne@notty
lavaren+ 13453  0.0  0.0   6500   632 pts/10   S+   23:48   0:00 ping -c 2 192.168.1.1
lavaren+ 13472  0.0  0.0  22684  1328 pts/0    R+   23:48   0:00 ps -aux

du coup le kill ne tue plus rien, là je suis perdu... c'est quoi ce bazar???
Je pense que le coeur de mon problème est ici et que le rajout de la boucle infinie est une fausse solution au problème du pid et que c'est ce qui m'empêche la fermeture correcte de mes threads.. possible??

Merci de l'aide!
smile


[edit]
Tiens, ne serait-il pas possible que la fin de la commande run() entraîne la fermeture du thread et du coup le processus vlc lancé serait mort-né???

[edit2]
Il y a bien un truc comme ça... Si je mets sleep 10 à la place de la boucle infinie, ça marche 10 secondes et mon processus vlc disparaît tout seul...
Apparemment, la fin de la commande run entraîne la mort de mon vlc... hmm

[edit3]
Bon, l'idée serait d'attendre tant que le fichier PID1.txt existe et s'il est supprimé (après le kill) de sortir de la boucle:

run("cvlc {} & echo $! > PID1.txt | while (true) do if [ -f PID1.txt ]; then sleep 1;else break; fi; done".format(self.IP_multicast[0]))

et en rajoutant un rm PID1.txt après le kill, j'ai l'impression que ça fonctionne... En tout cas ps -aux ne me donne plus de signe de vie de quoi que ce soit à l'intérieur de mon thread... smile

Mais pfff, il n'y aurait pas plus simple???
En plus je ne peux pas tester en conditions réelles (vacances..) hmm

Je suis preneur de toutes vos idées... Merci et bravo à ceux qui auront lu ce message jusqu'au bout..!! wink

Dernière modification par totola (Le 28/04/2015, à 01:02)

Hors ligne

#10 Le 28/04/2015, à 10:25

grim7reaper

Re : python: arrêter un thread

Salut,

Je pense qu’il va vraiment falloir appliquer les conseils du lien de la FAQ que tu m’avais donné. C’est la source de tout tes problèmes.
Parmis ce qui est proposé, dtach me semble le plus simple à utiliser.
Quelque chose dans ce genre là peut-être :

run("dtach -n /tmp/cvlc_bg -Ez cvlc {} & echo $! > PID1.txt".format(self.IP_multicast[0]))

Hors ligne

#11 Le 28/04/2015, à 11:03

totola

Re : python: arrêter un thread

OK merci, j'avais déjà regardé dtach mais j'avais pas reussi et abandonné ... Je vais essayer ça!

Sinon, après quelques tests, ma technique semble fonctionner... Mais bon je ne trouve pas ça super joli, devoir créer ce fichier pid, puis le supprimer... Et surtout je n'ai pas une grande confiance dans la fiabilité et la robustesse de ma méthode..
Dans l'idéal j'aimerais bien me passer de ce fichier d'ailleurs..

Merci pour les pistes, j'ai déjà bien avancé dans tous les cas!! smile

Hors ligne

#12 Le 28/04/2015, à 19:34

totola

Re : python: arrêter un thread

grim7reaper a écrit :

Salut,
Quelque chose dans ce genre là peut-être :

run("dtach -n /tmp/cvlc_bg -Ez cvlc {} & echo $! > PID1.txt".format(self.IP_multicast[0]))

C'est pareil... apparemment vlc se lance bien mais ça fait la même chose..
Mais comme je ne maîtrise pas dtach... Il faut vraiment que je me penche sérieusement là-dessus pour essayer d'adapter.
Merci en tout cas, j'ai quand même déjà bien avancé grâce à toi!

Hors ligne