¿Cuál es el uso de join () en subprocesos?

11 minutos de lectura

avatar de usuario de user192362127
usuario192362127

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

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

pero también lo he visto usando t.join() a pesar de t no estaba 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 se para que sirve t.join() ya que no es daemon y no puedo ver ningún cambio incluso si lo elimino

  • +1 por el título. ‘Unirse’ parece estar especialmente diseñado para fomentar un rendimiento deficiente (al crear/terminar/destruir subprocesos continuamente), bloqueos de GUI (esperando en controladores de eventos) y fallas de cierre de la aplicación (esperando a que terminen los subprocesos ininterrumpidos). Nota: no solo Python, este es un antipatrón entre idiomas.

    – Martín James

    26/02/2013 a las 11:00


  • Muchas respuestas solo dan lo que hace .join(). Pero creo que la pregunta real es cuál es el punto de .join() cuando parece tener el mismo efecto que ejecutar su secuencia de comandos sin subprocesos.

    – José

    10 dic 2020 a las 12:50

Avatar de usuario de Don Question
no pregunta

Un arte ascii algo torpe para demostrar el mecanismo: El join() presumiblemente es llamado por el hilo principal. También podría ser llamado por otro hilo, pero complicaría innecesariamente el diagrama.

join-la llamada debe colocarse en la pista del subproceso principal, pero para expresar la relación entre subprocesos y mantenerla lo más simple posible, elijo colocarla en el subproceso secundario.

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 subproceso principal no hace nada después de su join. Tu puedes decir join es (solo) relevante para el flujo de ejecución del subproceso principal.

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

  • ¿Confirme que se puede unir un subproceso daemonizado () sin bloquear la ejecución del programa?

    – Aviador45003

    3 de marzo de 2015 a las 8:52

  • @ Aviator45003: Sí, usando el argumento de tiempo de espera como: demon_thread.join(0.0), join() está bloqueando por defecto sin tener en cuenta el atributo daemonizado. ¡Pero unirse a un hilo demonizado probablemente abre una lata de problemas! Ahora estoy considerando quitar el join() llama a mi pequeño diagrama para el hilo del demonio…

    – Don pregunta

    3 de marzo de 2015 a las 17:15


  • @DonQuestion Entonces, si nos ponemos en daemon=True ¿No necesitamos el join() si necesitamos el join() al final del código?

    – Benyamin Jafari – aGn

    22 de enero de 2019 a las 8:32


  • @BenyaminJafari: Sí. Si no, entonces el subproceso principal (= programa) saldría, si solo queda el subproceso daemon. Pero la naturaleza de un subproceso de daemon (python) es que al subproceso principal no le importa si esta tarea en segundo plano aún se está ejecutando. Pensaré en cómo desarrollar eso en mi respuesta, para aclarar ese problema. ¡Gracias por tu comentario!

    – Don pregunta

    18 de marzo de 2019 a las 21:25


  • En el primer caso, cuando el main thread termina, el programa terminará sin dejar child-thread(long) terminar de ejecutarse solo (es decir, child-thread(long) no está completamente hecho)?

    – cielo arbol

    21 oct 2019 a las 20:51


avatar de usuario de dmg
DMG

Directamente desde el documentos

unirse([timeout]) Espere hasta que termine el subproceso. Esto bloquea el subproceso de llamada hasta que finaliza el subproceso cuyo método join() se llama, ya sea normalmente o a través de una excepción no controlada, o hasta que se agota el tiempo de espera opcional.

Esto significa que el hilo principal que genera t y despera t terminar hasta que termine.

Dependiendo de la lógica que emplee su programa, es posible que desee esperar hasta que finalice un subproceso antes de que continúe su subproceso principal.

También de los documentos:

Un subproceso se puede marcar como un “subproceso daemon”. La importancia de esta bandera es que todo el programa de Python se cierra cuando solo quedan subprocesos del 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 generará:

Test one
Test non-daemon
Test two

Aquí el subproceso maestro espera explícitamente el t hilo para terminar hasta que llame 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 el t hilo para terminar. En este caso, incluso podríamos eliminar la unión explícita. t.join() y el programa esperará implícitamente a t para terminar.

  • ¿Puedes hacer algún cambio en mi código para que pueda ver la diferencia de t.join(). añadiendo algo de sueño o algo más. en este momento puedo ver cualquier cambio en el programa, incluso si lo uso o no. pero para damemon puedo ver su salida si uso d.join() que no veo cuando no uso d.join()

    – usuario192362127

    26 de febrero de 2013 a las 9:46


Avatar de usuario de Kiki Jewell
kiki joya

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

Hoy aprendí algo sobre .join().

Estos hilos se ejecutan en paralelo:

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

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

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

En particular, estaba tratando de ser inteligente 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 self.start() en __ init __, pero no self.join(). eso hay que hacerlo después cada hilo ha sido iniciado.

join() es lo que hace que el subproceso principal espere a que termine su subproceso. De lo contrario, su hilo se ejecuta solo.

Entonces, una forma de pensar en join() como una “retención” en el subproceso principal: deshace el subproceso y se ejecuta secuencialmente en el subproceso principal, antes de que el subproceso principal pueda continuar. Asegura que su hilo esté completo antes de que el hilo principal avance. Tenga en cuenta que esto significa que está bien si su subproceso ya está terminado antes de llamar a join(): el subproceso principal simplemente se libera inmediatamente cuando se llama a join().

De hecho, ahora se me ocurre que el subproceso principal espera en d.join() hasta que el subproceso d finaliza 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 declaraciones de impresión se entrelazan 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() está retrasando el hilo principal. Los tres subprocesos se completan antes de que finalice t1.join() y el subproceso principal pasa a ejecutar print, luego t2.join(), luego print, luego t3.join() y luego print.

Correcciones bienvenidas. También soy nuevo en hilos.

(Nota: en caso de que esté interesado, estoy escribiendo un código para un DrinkBot, y necesito subprocesos para ejecutar las bombas de ingredientes simultáneamente en lugar de secuencialmente, menos tiempo para esperar cada bebida).

  • Oye, también soy nuevo en Python Threading y estoy confundido acerca del hilo principal. ¿El primer hilo es el hilo principal? Si no, ¿me guían?

    – Rohit Khatri

    11 de agosto de 2016 a las 6:14

  • El hilo principal es el propio programa. Cada uno de los hilos se bifurca desde allí. Luego se vuelven a unir, porque en el comando join(), el programa espera hasta que el hilo finaliza antes de continuar ejecutándose.

    – Kiki Jewell

    3 de enero de 2017 a las 5:16

  • Creo que la gran pregunta es por qué querrías detener el subproceso principal cuando el objetivo de subprocesos es ejecutarse en paralelo. Creo que la respuesta a join() es que puede querer ejecutar partes de su programa en paralelo, pero puede llegar a una parte de su subproceso principal que requiere el resultado de su subproceso antes de continuar.

    – José

    1 oct 2021 a las 11:58

Avatar de usuario de Ketouem
Ketouem

El método unir()

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

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

Avatar de usuario de Mohideen bin Mohammed
Mohideen bin Mohamed

Con unirse: el intérprete esperará hasta que su proceso llegue terminado o terminado

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

sin unirse: el intérprete no esperará hasta que se obtenga el proceso terminado,

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

Avatar de usuario de Shishir Nanoty
Shishir Nanoty

En python 3.x, join() se usa para unir un subproceso con el subproceso principal, es decir, cuando se usa join() para un subproceso en particular, el subproceso principal dejará de ejecutarse hasta que se complete la ejecución del subproceso 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! 

'''

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

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 is killed, this thread will be killed as well. 
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

¿Ha sido útil esta solución?