¿Cuál es el uso de join () en el subproceso de python

Estaba estudiando el enhebrado de python y me encontré con join() .

El autor dijo que si el subproceso está en modo daemon, entonces necesito usar join() para que el subproceso pueda completarse antes de que finalice el subproceso principal.

pero también lo he visto usando t.join() aunque t no era un daemon

código de ejemplo es este

 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() 

No sé qué es el uso de t.join() ya que no es un demonio y no puedo ver ningún cambio incluso si lo quito.

Un ascii-art un tanto torpe para demostrar el mecanismo: el join() es presumiblemente llamado por el hilo principal. También podría ser llamado por otro hilo, pero complicaría innecesariamente el diagtwig.

join -calling debe colocarse en la pista del hilo principal, pero para express la relación hilo y mantenerlo lo más simple posible, elijo colocarlo en el hilo hijo.

 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 

Entonces, la razón por la que no ve ningún cambio es porque su hilo principal no hace nada después de su join . Se podría decir que join es (solo) relevante para el flujo de ejecución del hilo principal.

Si, por ejemplo, desea descargar simultáneamente un grupo de páginas para concatenarlas en una sola página grande, puede iniciar descargas simultáneas utilizando subprocesos, pero debe esperar hasta que la última página / subproceso finalice antes de comenzar a armar una sola página. Fuera de muchos. Ahí es cuando usas join() .

Directamente de los documentos

unirse ([timeout]) Espere hasta que el hilo termine. Esto bloquea el subproceso de llamada hasta que el subproceso cuyo método de combinación () se termina, ya sea normalmente o mediante una excepción no controlada, o hasta que se produzca el tiempo de espera opcional.

Esto significa que el hilo principal que genera t y d , espera a que t termine hasta que termine.

Dependiendo de la lógica que emplee su progtwig, es posible que desee esperar hasta que un hilo termine antes de que su hilo principal continúe.

También desde la documentación:

Un hilo se puede marcar como un “hilo daemon”. El significado de este indicador es que todo el progtwig de Python se cierra cuando solo quedan hilos de daemon.

Un ejemplo simple, digamos que tenemos esto:

 def non_daemon(): time.sleep(5) print 'Test non-daemon' t = threading.Thread(name='non-daemon', target=non_daemon) t.start() 

Que termina con:

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

Esto dará como resultado:

 Test one Test non-daemon Test two 

Aquí, el hilo maestro espera explícitamente a que el hilo t termine hasta que se print la segunda vez.

Alternativamente si tuviéramos esto:

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

Obtendremos esta salida:

 Test one Test two Test non-daemon 

Aquí hacemos nuestro trabajo en el hilo principal y luego esperamos a que termine el hilo t . En este caso, podríamos incluso eliminar la unión explícita t.join() y el progtwig esperará implícitamente a que t termine.

Gracias por este hilo – también me ayudó mucho.

Aprendí algo sobre .join () hoy.

Estos hilos se ejecutan en paralelo:

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

y estos se ejecutan secuencialmente (no lo que yo quería):

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

En particular, estaba tratando de ingenioso y ordenado:

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

¡Esto funciona! Pero se ejecuta secuencialmente. Puedo poner el self.start () en __ init __, pero no el self.join (). Eso debe hacerse después de que se haya iniciado cada hilo.

join () es lo que hace que el hilo principal espere a que termine tu hilo. De lo contrario, su hilo se ejecuta por sí mismo.

Por lo tanto, una forma de pensar en join () como una “retención” en el hilo principal: de algún modo, desenrosca el hilo y se ejecuta de forma secuencial en el hilo principal, antes de que el hilo principal pueda continuar. Asegura que tu hilo esté completo antes de que el hilo principal avance. Tenga en cuenta que esto significa que está bien si su hilo ya está terminado antes de llamar a join (): el hilo principal simplemente se libera inmediatamente cuando se llama join ().

De hecho, ahora se me ocurre que el hilo principal espera en d.join () hasta que el hilo d termina antes de pasar a t.join ().

De hecho, para ser muy claro, considere este código:

 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" 

Produce esta salida (observe cómo las instrucciones de impresión se enlazan entre sí).

 $ 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 $ 

El t1.join () sostiene el hilo principal. Los tres subprocesos se completan antes de que t1.join () finalice y el hilo principal se mueva para ejecutar la impresión, luego t2.join () luego imprima y luego t3.join () luego imprima.

Correcciones de bienvenida También soy nuevo en enhebrar.

(Nota: en caso de que esté interesado, estoy escribiendo el código para un DrinkBot, y necesito un hilo para ejecutar las bombas de ingredientes al mismo tiempo en lugar de hacerlo de forma secuencial, menos tiempo para esperar cada bebida).

El método join ()

bloquea el subproceso de llamada hasta que finaliza el subproceso cuyo método join () se termina.

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

Cuando se realiza la función join(t) tanto para el hilo no daemon como para el hilo daemon, el hilo principal (o proceso principal) debe esperar t segundos, y luego puede ir más allá para trabajar en su propio proceso. Durante el tiempo de espera de t segundos, ambos subprocesos secundarios deben hacer lo que pueden hacer, como imprimir un texto. Después de los t segundos, si el subproceso no daemon aún no terminó su trabajo, y todavía puede finalizarlo después de que el proceso principal finalice su trabajo, pero para el subproceso del daemon, simplemente perdió su ventana de oportunidad. Sin embargo, eventualmente morirá después de que el progtwig Python salga. Por favor corrígeme si hay algo mal.

En Python 3.x, join () se usa para unir un hilo con el hilo principal, es decir, cuando join () se usa para un hilo en particular, el hilo principal dejará de ejecutarse hasta que se complete la ejecución del hilo unido.

 #1 - Without Join(): import threading import time def loiter(): print('You are loitering!') time.sleep(5) print('You are not loitering anymore!') t1 = threading.Thread(target = loiter) t1.start() print('Hey, I do not want to loiter!') ''' Output without join()--> You are loitering! Hey, I do not want to loiter! You are not loitering anymore! #After 5 seconds --> This statement will be printed ''' #2 - With Join(): import threading import time def loiter(): print('You are loitering!') time.sleep(5) print('You are not loitering anymore!') t1 = threading.Thread(target = loiter) t1.start() t1.join() print('Hey, I do not want to loiter!') ''' Output with join() --> You are loitering! You are not loitering anymore! #After 5 seconds --> This statement will be printed Hey, I do not want to loiter! ''' 

Entendimiento simple,

con unirse: el intérprete esperará hasta que su proceso se complete o finalice

 >>> 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 ie after 10sec waiting for 10sec >>> done? 

sin unirse – el intérprete no esperará hasta que el proceso termine ,

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

Este ejemplo demuestra la acción .join() :

 import threading import time def threaded_worker(): for r in range(10): print('Other: ', r) time.sleep(2) thread_ = threading.Timer(1, threaded_worker) thread_.daemon = True # If the main thread kills, this thread will be killed too. thread_.start() flag = True for i in range(10): print('Main: ', i) time.sleep(2) if flag and i > 4: print( ''' Threaded_worker() joined to the main thread. Now we have a sequential behavior instead of concurrency. ''') thread_.join() flag = False 

Afuera:

 Main: 0 Other: 0 Main: 1 Other: 1 Main: 2 Other: 2 Main: 3 Other: 3 Main: 4 Other: 4 Main: 5 Other: 5 Threaded_worker() joined to the main thread. Now we have a sequential behavior instead of concurrency. Other: 6 Other: 7 Other: 8 Other: 9 Main: 6 Main: 7 Main: 8 Main: 9 

“¿Para qué sirve utilizar join ()?” tu dices. Realmente, es la misma respuesta que “¿para qué sirve cerrar archivos, ya que Python y el sistema operativo cerrarán mi archivo cuando mi progtwig se cierre?”.

Es simplemente una cuestión de buena progtwigción. Debería unir () sus hilos en el punto en el código que el hilo ya no debería estar ejecutándose, ya sea porque tiene que asegurarse de que el hilo no se está ejecutando para interferir con su propio código, o que desea comportarse correctamente en una sistema más grande

Podría decir “No quiero que mi código se demore en dar una respuesta” solo por el tiempo adicional que puede requerir la combinación (). Esto puede ser perfectamente válido en algunos escenarios, pero ahora debe tener en cuenta que su código “deja a Cruft para que Python y el sistema operativo se limpien”. Si lo hace por motivos de rendimiento, le recomiendo que documente ese comportamiento. Esto es especialmente cierto si está construyendo una biblioteca / paquete que se espera que otros utilicen.

No hay ninguna razón para no unirse (), aparte de las razones de rendimiento, y diría que su código no tiene por qué funcionar tan bien.