Foros Python

Versión completa: Condicionar un Hilo desde otro Hilo con Threat
Actualmente estas viendo una versión simplificada de nuestro contenido. Ver la versión completa con el formato correcto.
Hola a todos una vez mas, me esta gustando mucho este lenguaje de programacion! el motivo esta vez es referente a un problema con los thread o multihilos. Mi problema es sencillo pero a la vez complicado, ya que he buscado en varios lugares pero no encuentro la solución o simplemente no la entiendo.
La circunstancia es la siguiente:

Tengo dos hilos ejecutándose simultáneamente y quiero que uno de los hilos se pause durante un tiempo cuando el otro hilo lo pida. Utilizo la librería “thread” de python y he estado leyendo e intentando hacer ejemplos con las funciones “lock”, “event” y “condition” pero no las entiendo. Lo mas que he hecho ha sido parar la función que no quiero que se pare.

Utilizo windows 10 con python 2.7 y el ide es el que viene con python por defecto. Aquí os dejo un trozo de código de uno de los ejemplos que he estado trabajando para aprender, ya que luego lo quiero extrapolar a un script mayor que estoy haciendo.


Quiero que desde la función “letras” pueda pausar la función “números” durante unos cinco segundos pero sin parar la función letras.

Código:
import threading
import time



def letras(evento):
 
   abecedario = ['a','b','c','d','e','f','g','h','i','j']

   for i in range(len(abecedario)):

       if abecedario[i] == 'c':
                 
       # Aqui es donde quiero detener el otro hilo "numeros" durante
       # unos 5 segundos

                         
       else:
           
           print abecedario[i]

           time.sleep(1)




def numeros(evento):

       
   numbers= [1,2,3,4,5,6,7,8,9,10]

   for j in range(len(numbers)):

       print numbers[j]
       time.sleep(1)
            


       
if __name__ == "__main__" :

   
   evento = threading.Event()
   
   hilo1 = threading.Thread(target=letras, args=(evento,))  
   hilo2 = threading.Thread(target=numeros, args=(evento,))

   hilo1.daemon = True
   hilo1.start()
   hilo2.daemon = True
   hilo2.start()


Muchas gracias una vez mas por vuestra atención. Saludos
Hola, ¿cómo estás?

Efectivamente threading.Event es la forma correcta de realizar el tipo de sincronización que estás buscando. Únicamente te está faltando escribir y leer su valor en los hilos correspondientes. Para que sea más claro, te recomiendo que llames al evento detener_numeros.

Código:
if __name__ == "__main__" :
   detener_numeros = threading.Event()

   hilo1 = threading.Thread(target=letras, args=(detener_numeros,))  
   hilo2 = threading.Thread(target=numeros, args=(detener_numeros,))

Ahora bien, un evento puede tener dos estados: desactivado/apagado/falso (que es el valor inicial) o activado/encendido/verdadero. De modo que en el lugar en donde quieres detener el hilo "números", vamos a establecer el valor de "activado" vía la función Event.set.

Código:
def letras(detener_numeros):
   abecedario = ['a','b','c','d','e','f','g','h','i','j']

   for i in range(len(abecedario)):
       if abecedario[i] == 'c':
           # Aqui es donde quiero detener el otro hilo "numeros" durante
           # unos 5 segundos
           detener_numeros.set()
       else:
           print abecedario[i]
       time.sleep(1)

Y en el otro hilo, vamos a chequear periódicamente el valor del evento para que, en caso de estar activado, suspendamos la ejecución por 5 segundos.

Código:
def numeros(detener_numeros):
   numbers= [1,2,3,4,5,6,7,8,9,10]

   for j in range(len(numbers)):
       if detener_numeros.is_set():
           print "Detenido."
           time.sleep(5)
           detener_numeros.clear()
       print numbers[j]
       time.sleep(1)

Una vez transcurridos los 5 segundos, restablecemos el valor del evento vía Event.clear() para que no vuelva a suspenderse en la próxima iteración.

Te comento otros dos problemas que observé en tu código. Primero, al ejecutar los hilos como daemon = True no estás esperando a que finalicen, por lo que tu script termina inmediatamente luego de lanzar los hilos. Debes esperar a que terminen vía la función Thread.join.

Código:
if __name__ == "__main__" :
   detener_numeros = threading.Event()

   hilo1 = threading.Thread(target=letras, args=(detener_numeros,))  
   hilo2 = threading.Thread(target=numeros, args=(detener_numeros,))

   hilo1.start()
   hilo2.start()

   hilo1.join()
   hilo2.join()

Por otro lado, veo que estás usando tres espacios para delimitar los bloques de tu código. Lo recomendado es siempre usar 4 espacios (seguramente puedas configurar esto desde tu IDE).

El código completo sería:

Código:
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import threading
import time


def letras(detener_numeros):
    abecedario = ['a','b','c','d','e','f','g','h','i','j']

    for i in range(len(abecedario)):
        if abecedario[i] == 'c':
            # Aqui es donde quiero detener el otro hilo "numeros" durante
            # unos 5 segundos
            detener_numeros.set()
        else:
            print abecedario[i]
        time.sleep(1)


def numeros(detener_numeros):
    numbers= [1,2,3,4,5,6,7,8,9,10]

    for j in range(len(numbers)):
        if detener_numeros.is_set():
            print "Detener."
            time.sleep(5)
            detener_numeros.clear()
        print numbers[j]
        time.sleep(1)


if __name__ == "__main__" :
    detener_numeros = threading.Event()

    hilo1 = threading.Thread(target=letras, args=(detener_numeros,))  
    hilo2 = threading.Thread(target=numeros, args=(detener_numeros,))

    hilo1.start()
    hilo2.start()

    hilo1.join()
    hilo2.join()


Un saludo.
Hola Francisco, primero que nada disculparme por haber tardado en contestar y es que he tenido una semana complicada. Como siempre muchas gracias, ha funcionado perfectamente y explicado de una manera sencilla, que es lo que mas agradezco. Posiblemente vuelva a tener alguna duda con respecto al tema de multihilos así que seguramente postearé alguna que otra vez, ¿conoces alguna bibliografía o algo que lo explique de una manera sencilla para iniciados?

Muchas gracias por tu atención.
Hola nuevamente. Me alegro que te haya funcionado y no dudes en publicar cualquier otra consulta.

Respecto a la bibliografía siempre es bueno remitirse a la documentación oficial del módulo (en inglés), aunque tiene una estructura bien determinada, puede pasar detalles por alto y, por lo general, no es muy versátil. Te podría recomendar Threads en Python del autor del libro "Python para todos" que seguramente te resulte más didáctico. Es un artículo del 2008 pero la API no ha cambiado mucho desde entonces.

¡Saludos!