273 votes

Quelle est l'utilité de join() dans le threading Python ?

J'étudiais le threading de python et je suis tombé sur join() .

L'auteur a dit que si le thread est en mode démon, je dois utiliser join() pour que ce thread puisse se terminer avant que le thread principal ne se termine.

mais je l'ai aussi vu utiliser t.join() même si t n'était pas daemon

Voici un exemple de code

import threading
import time
import logging

logging.basicConfig(level=logging.DEBUG,
                    format='(%(threadName)-10s) %(message)s',
                    )

def daemon():
    logging.debug('Starting')
    time.sleep(2)
    logging.debug('Exiting')

d = threading.Thread(name='daemon', target=daemon)
d.setDaemon(True)

def non_daemon():
    logging.debug('Starting')
    logging.debug('Exiting')

t = threading.Thread(name='non-daemon', target=non_daemon)

d.start()
t.start()

d.join()
t.join()

Je ne sais pas à quoi ça sert t.join() car ce n'est pas un démon et je ne vois aucun changement même si je le supprime.

376voto

Don Question Points 3856

Un ascii-art quelque peu maladroit pour démontrer le mécanisme : Le site join() est vraisemblablement appelé par le main-thread. Il pourrait également être appelé par un autre thread, mais cela compliquerait inutilement le diagramme.

join -L'appel devrait être placé dans la piste du fil principal, mais pour exprimer la relation entre les fils et rester aussi simple que possible, je choisis de le placer dans le fil enfant à la place.

without join:
+---+---+------------------                     main-thread
    |   |
    |   +...........                            child-thread(short)
    +..................................         child-thread(long)

with join
+---+---+------------------***********+###      main-thread
    |   |                             |
    |   +...........join()            |         child-thread(short)
    +......................join()......         child-thread(long)

with join and daemon thread
+-+--+---+------------------***********+###     parent-thread
  |  |   |                             |
  |  |   +...........join()            |        child-thread(short)
  |  +......................join()......        child-thread(long)
  +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,     child-thread(long + daemonized)

'-' main-thread/parent-thread/main-program execution
'.' child-thread execution
'#' optional parent-thread execution after join()-blocked parent-thread could 
    continue
'*' main-thread 'sleeping' in join-method, waiting for child-thread to finish
',' daemonized thread - 'ignores' lifetime of other threads;
    terminates when main-programs exits; is normally meant for 
    join-independent tasks

Ainsi, la raison pour laquelle vous ne voyez aucun changement est que votre thread principal ne fait rien après que votre join . Vous pourriez dire join est (uniquement) pertinent pour le flux d'exécution du fil principal.

Si, par exemple, vous souhaitez télécharger simultanément un certain nombre de pages pour les concaténer en une seule grande page, vous pouvez commencer les téléchargements simultanés en utilisant des threads, mais vous devez attendre que la dernière page/thread soit terminée avant de commencer à assembler une seule page à partir de plusieurs. C'est alors que vous utilisez join() .

89voto

DJV Points 2028

En direct de la docs

join([timeout]) Attendez que le thread se termine. Cela bloque le thread appelant jusqu'à ce que le thread dont la méthode join() est appelée se termine - soit normalement, soit par une exception non gérée - ou jusqu'à ce que le délai d'attente optionnel se produise.

Cela signifie que le thread principal qui se crée t y d attend t jusqu'à ce qu'elle se termine.

Selon la logique employée par votre programme, vous voudrez peut-être attendre qu'un thread se termine avant que votre thread principal ne continue.

Aussi dans les docs :

Un thread peut être marqué comme un "thread démon". La signification de cet indicateur est que le programme Python entier se termine lorsqu'il ne reste plus que des threads démons.

Un exemple simple, disons que nous avons ceci :

def non_daemon():
    time.sleep(5)
    print 'Test non-daemon'

t = threading.Thread(name='non-daemon', target=non_daemon)

t.start()

Qui se termine par :

print 'Test one'
t.join()
print 'Test two'

Ceci produira un résultat :

Test one
Test non-daemon
Test two

Ici, le fil d'exécution principal attend explicitement que l'élément t pour terminer jusqu'à ce qu'il appelle print la deuxième fois.

Ou alors, si on avait ça :

print 'Test one'
print 'Test two'
t.join()

Nous obtiendrons cette sortie :

Test one
Test two
Test non-daemon

Ici, nous effectuons notre travail dans le fil d'exécution principal, puis nous attendons que le système d'exploitation de l'entreprise soit activé. t fil pour finir. Dans ce cas, nous pourrions même supprimer la jonction explicite t.join() et le programme attendra implicitement t pour finir.

53voto

Kiki Jewell Points 366

Merci pour ce fil de discussion - il m'a beaucoup aidé aussi.

J'ai appris quelque chose sur .join() aujourd'hui.

Ces fils fonctionnent en parallèle :

d.start()
t.start()
d.join()
t.join()

et ceux-ci s'exécutent de manière séquentielle (ce qui n'est pas ce que je voulais) :

d.start()
d.join()
t.start()
t.join()

En particulier, j'essayais d'être astucieux et ordonné :

class Kiki(threading.Thread):
    def __init__(self, time):
        super(Kiki, self).__init__()
        self.time = time
        self.start()
        self.join()

Cela fonctionne ! Mais il s'exécute de manière séquentielle. Je peux mettre le self.start() dans __ init __, mais pas le self.join(). Cela doit être fait après tous les sujets ont été abordés.

join() est ce qui fait que le thread principal attend que votre thread se termine. Dans le cas contraire, votre thread s'exécute tout seul.

On peut donc considérer join() comme un "hold" sur le fil d'exécution principal - il désenchevêtre en quelque sorte votre fil d'exécution et s'exécute séquentiellement dans le fil d'exécution principal, avant que celui-ci ne puisse continuer. Il s'assure que votre thread est complet avant que le thread principal n'avance. Notez que cela signifie qu'il n'y a pas de problème si votre thread est déjà terminé avant que vous appeliez join() -- le thread principal est simplement libéré immédiatement lorsque join() est appelé.

En fait, je viens de me rendre compte que le fil principal attend que le fil d.join() se termine avant de passer à t.join().

En fait, pour être très clair, considérez ce code :

import threading
import time

class Kiki(threading.Thread):
    def __init__(self, time):
        super(Kiki, self).__init__()
        self.time = time
        self.start()

    def run(self):
        print self.time, " seconds start!"
        for i in range(0,self.time):
            time.sleep(1)
            print "1 sec of ", self.time
        print self.time, " seconds finished!"

t1 = Kiki(3)
t2 = Kiki(2)
t3 = Kiki(1)
t1.join()
print "t1.join() finished"
t2.join()
print "t2.join() finished"
t3.join()
print "t3.join() finished"

Il produit cette sortie (notez comment les instructions d'impression sont imbriquées les unes dans les autres).

$ python test_thread.py
32   seconds start! seconds start!1

 seconds start!
1 sec of  1
 1 sec of 1  seconds finished!
 21 sec of
3
1 sec of  3
1 sec of  2
2  seconds finished!
1 sec of  3
3  seconds finished!
t1.join() finished
t2.join() finished
t3.join() finished
$ 

Le t1.join() retarde le fil principal. Les trois threads se terminent avant que t1.join() ne se termine et le thread principal passe à l'exécution de print puis t2.join() puis print puis t3.join() puis print.

Les corrections sont les bienvenues. Je suis également novice en matière d'enfilage.

(Note : au cas où cela vous intéresserait, j'écris du code pour un DrinkBot, et j'ai besoin de threading pour faire fonctionner les pompes à ingrédients simultanément plutôt que séquentiellement -- moins de temps à attendre pour chaque boisson).

23voto

Ketouem Points 1390

La méthode join()

bloque le thread appelant jusqu'à ce que le thread dont la méthode join() est appelée soit terminé.

Source : http://docs.python.org/2/library/threading.html

12voto

Avec join - l'interprète va attendre que votre processus obtienne terminé o terminé

>>> from threading import Thread
>>> import time
>>> def sam():
...   print 'started'
...   time.sleep(10)
...   print 'waiting for 10sec'
... 
>>> t = Thread(target=sam)
>>> t.start()
started

>>> t.join() # with join interpreter will wait until your process get completed or terminated
done?   # this line printed after thread execution stopped i.e after 10sec
waiting for 10sec
>>> done?

sans jointure - l'interpréteur n'attendra pas que le processus soit terminé terminé ,

>>> t = Thread(target=sam)
>>> t.start()
started
>>> print 'yes done' #without join interpreter wont wait until process get terminated
yes done
>>> waiting for 10sec

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X