Implementación de subprocesos

En este capítulo, aprenderemos cómo implementar subprocesos en Python.

Módulo de Python para la implementación de subprocesos

Los subprocesos de Python a veces se denominan procesos ligeros porque ocupan mucha menos memoria que los procesos. Los hilos permiten realizar múltiples tareas a la vez. En Python, tenemos los siguientes dos módulos que implementan subprocesos en un programa:

  • <_thread>module

  • <threading>module

La principal diferencia entre estos dos módulos es que <_thread> El módulo trata un hilo como una función, mientras que el <threading>El módulo trata cada hilo como un objeto y lo implementa de una manera orientada a objetos. Además, el<_thread>El módulo es eficaz en subprocesos de bajo nivel y tiene menos capacidades que el <threading> módulo.

módulo <_thread>

En la versión anterior de Python, teníamos la <thread>módulo, pero se ha considerado "obsoleto" durante bastante tiempo. Se ha animado a los usuarios a utilizar<threading>módulo en su lugar. Por lo tanto, en Python 3 el módulo "hilo" ya no está disponible. Se le cambió el nombre a "<_thread>"para las incompatibilidades con versiones anteriores en Python3.

Para generar un nuevo hilo con la ayuda del <_thread> módulo, necesitamos llamar al start_new_threadmétodo de la misma. El funcionamiento de este método se puede entender con la ayuda de la siguiente sintaxis:

_thread.start_new_thread ( function, args[, kwargs] )

Aquí -

  • args es una tupla de argumentos

  • kwargs es un diccionario opcional de argumentos de palabras clave

Si queremos llamar a la función sin pasar un argumento, entonces necesitamos usar una tupla vacía de argumentos en args.

Esta llamada al método regresa inmediatamente, el hilo secundario comienza y llama a la función con la lista pasada, si la hay, de argumentos. El hilo termina cuando la función regresa.

Ejemplo

A continuación, se muestra un ejemplo para generar un nuevo hilo utilizando el <_thread>módulo. Estamos usando el método start_new_thread () aquí.

import _thread
import time

def print_time( threadName, delay):
   count = 0
   while count < 5:
      time.sleep(delay)
      count += 1
      print ("%s: %s" % ( threadName, time.ctime(time.time()) ))

try:
   _thread.start_new_thread( print_time, ("Thread-1", 2, ) )
   _thread.start_new_thread( print_time, ("Thread-2", 4, ) )
except:
   print ("Error: unable to start thread")
while 1:
   pass

Salida

El siguiente resultado nos ayudará a comprender la generación de nuevos subprocesos b con la ayuda del <_thread> módulo.

Thread-1: Mon Apr 23 10:03:33 2018
Thread-2: Mon Apr 23 10:03:35 2018
Thread-1: Mon Apr 23 10:03:35 2018
Thread-1: Mon Apr 23 10:03:37 2018
Thread-2: Mon Apr 23 10:03:39 2018
Thread-1: Mon Apr 23 10:03:39 2018
Thread-1: Mon Apr 23 10:03:41 2018
Thread-2: Mon Apr 23 10:03:43 2018
Thread-2: Mon Apr 23 10:03:47 2018
Thread-2: Mon Apr 23 10:03:51 2018

módulo <threading>

los <threading>El módulo se implementa de una manera orientada a objetos y trata cada hilo como un objeto. Por lo tanto, proporciona un soporte de alto nivel mucho más potente para subprocesos que el módulo <_thread>. Este módulo se incluye con Python 2.4.

Métodos adicionales en el módulo <threading>

los <threading> módulo comprende todos los métodos del <_thread>módulo, pero también proporciona métodos adicionales. Los métodos adicionales son los siguientes:

  • threading.activeCount() - Este método devuelve el número de objetos de hilo que están activos.

  • threading.currentThread() - Este método devuelve el número de objetos de hilo en el control de hilo del llamador.

  • threading.enumerate() - Este método devuelve una lista de todos los objetos de hilo que están actualmente activos.

  • Para implementar subprocesos, el <threading> módulo tiene el Thread clase que proporciona los siguientes métodos:

    • run() - El método run () es el punto de entrada para un hilo.

    • start() - El método start () inicia un hilo llamando al método run.

    • join([time]) - El join () espera a que terminen los hilos.

    • isAlive() - El método isAlive () comprueba si un hilo aún se está ejecutando.

    • getName() - El método getName () devuelve el nombre de un hilo.

    • setName() - El método setName () establece el nombre de un hilo.

¿Cómo crear hilos usando el módulo <threading>?

En esta sección, aprenderemos cómo crear hilos usando el <threading>módulo. Siga estos pasos para crear un nuevo hilo usando el módulo <threading> -

  • Step 1 - En este paso, necesitamos definir una nueva subclase de la Thread clase.

  • Step 2 - Luego, para agregar argumentos adicionales, necesitamos anular el __init__(self [,args]) método.

  • Step 3 - En este paso, necesitamos anular el método run (self [, args]) para implementar lo que debería hacer el hilo cuando se inicia.

  • Ahora, después de crear el nuevo Thread subclase, podemos crear una instancia de la misma y luego iniciar un nuevo hilo invocando el start(), que a su vez llama al run() método.

Ejemplo

Considere este ejemplo para aprender cómo generar un nuevo hilo usando el <threading> módulo.

import threading
import time
exitFlag = 0

class myThread (threading.Thread):
   def __init__(self, threadID, name, counter):
      threading.Thread.__init__(self)
      self.threadID = threadID
      self.name = name
      self.counter = counter
   def run(self):
      print ("Starting " + self.name)
      print_time(self.name, self.counter, 5)
      print ("Exiting " + self.name)
def print_time(threadName, delay, counter):
   while counter:
      if exitFlag:
         threadName.exit()
      time.sleep(delay)
      print ("%s: %s" % (threadName, time.ctime(time.time())))
      counter -= 1

thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

thread1.start()
thread2.start()
thread1.join()
thread2.join()
print ("Exiting Main Thread")
Starting Thread-1
Starting Thread-2

Salida

Ahora, considere el siguiente resultado:

Thread-1: Mon Apr 23 10:52:09 2018
Thread-1: Mon Apr 23 10:52:10 2018
Thread-2: Mon Apr 23 10:52:10 2018
Thread-1: Mon Apr 23 10:52:11 2018
Thread-1: Mon Apr 23 10:52:12 2018
Thread-2: Mon Apr 23 10:52:12 2018
Thread-1: Mon Apr 23 10:52:13 2018
Exiting Thread-1
Thread-2: Mon Apr 23 10:52:14 2018
Thread-2: Mon Apr 23 10:52:16 2018
Thread-2: Mon Apr 23 10:52:18 2018
Exiting Thread-2
Exiting Main Thread

Programa Python para varios estados de subprocesos

Hay cinco estados de subproceso: nuevo, ejecutable, en ejecución, en espera y muerto. Entre estos cinco De estos cinco, nos centraremos principalmente en tres estados: en ejecución, en espera y muertos. Un hilo obtiene sus recursos en estado de ejecución, espera los recursos en estado de espera; la liberación final del recurso, si se está ejecutando y adquirido está en estado muerto.

El siguiente programa de Python con la ayuda de los métodos start (), sleep () y join () mostrará cómo un hilo entró en estado de ejecución, espera y muerto, respectivamente.

Step 1 - Importar los módulos necesarios, <threading> y <time>

import threading
import time

Step 2 - Defina una función, que se llamará al crear un hilo.

def thread_states():
   print("Thread entered in running state")

Step 3 - Estamos usando el método sleep () del módulo de tiempo para hacer que nuestro hilo espere, digamos, 2 segundos.

time.sleep(2)

Step 4 - Ahora, estamos creando un hilo llamado T1, que toma el argumento de la función definida anteriormente.

T1 = threading.Thread(target=thread_states)

Step 5- Ahora, con la ayuda de la función start () podemos iniciar nuestro hilo. Producirá el mensaje que hemos establecido al definir la función.

T1.start()
Thread entered in running state

Step 6 - Ahora, por fin podemos matar el hilo con el método join () después de que finalice su ejecución.

T1.join()

Comenzando un hilo en Python

En Python, podemos iniciar un nuevo hilo de diferentes formas, pero la más fácil de ellas es definirlo como una única función. Después de definir la función, podemos pasar esto como el objetivo de un nuevothreading.Threadobjeto y así sucesivamente. Ejecute el siguiente código de Python para comprender cómo funciona la función:

import threading
import time
import random
def Thread_execution(i):
   print("Execution of Thread {} started\n".format(i))
   sleepTime = random.randint(1,4)
   time.sleep(sleepTime)
   print("Execution of Thread {} finished".format(i))
for i in range(4):
   thread = threading.Thread(target=Thread_execution, args=(i,))
   thread.start()
   print("Active Threads:" , threading.enumerate())

Salida

Execution of Thread 0 started
Active Threads:
   [<_MainThread(MainThread, started 6040)>,
      <HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
      <Thread(Thread-3576, started 3932)>]

Execution of Thread 1 started
Active Threads:
   [<_MainThread(MainThread, started 6040)>,
      <HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
      <Thread(Thread-3576, started 3932)>,
      <Thread(Thread-3577, started 3080)>]

Execution of Thread 2 started
Active Threads:
   [<_MainThread(MainThread, started 6040)>,
      <HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
      <Thread(Thread-3576, started 3932)>,
      <Thread(Thread-3577, started 3080)>,
      <Thread(Thread-3578, started 2268)>]

Execution of Thread 3 started
Active Threads:
   [<_MainThread(MainThread, started 6040)>,
      <HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
      <Thread(Thread-3576, started 3932)>,
      <Thread(Thread-3577, started 3080)>,
      <Thread(Thread-3578, started 2268)>,
      <Thread(Thread-3579, started 4520)>]
Execution of Thread 0 finished
Execution of Thread 1 finished
Execution of Thread 2 finished
Execution of Thread 3 finished

Hilos de demonio en Python

Antes de implementar los subprocesos del demonio en Python, necesitamos conocer los subprocesos del demonio y su uso. En términos de computación, el daemon es un proceso en segundo plano que maneja las solicitudes de varios servicios como el envío de datos, transferencias de archivos, etc. Estaría inactivo si ya no se necesita. La misma tarea se puede realizar también con la ayuda de subprocesos que no son demonios. Sin embargo, en este caso, el hilo principal tiene que realizar un seguimiento de los hilos que no son demonio manualmente. Por otro lado, si estamos usando subprocesos de demonio, el subproceso principal puede olvidarse por completo de esto y se eliminará cuando el subproceso principal salga. Otro punto importante sobre los subprocesos de demonios es que podemos optar por usarlos solo para tareas no esenciales que no nos afectarían si no se completan o se eliminan en el medio. A continuación se muestra la implementación de subprocesos de demonio en Python:

import threading
import time

def nondaemonThread():
   print("starting my thread")
   time.sleep(8)
   print("ending my thread")
def daemonThread():
   while True:
   print("Hello")
   time.sleep(2)
if __name__ == '__main__':
   nondaemonThread = threading.Thread(target = nondaemonThread)
   daemonThread = threading.Thread(target = daemonThread)
   daemonThread.setDaemon(True)
   daemonThread.start()
   nondaemonThread.start()

En el código anterior, hay dos funciones a saber >nondaemonThread() y >daemonThread(). La primera función imprime su estado y duerme después de 8 segundos, mientras que la función deamonThread () imprime Hola después de cada 2 segundos de forma indefinida. Podemos entender la diferencia entre subprocesos que no son demonios y demonios con la ayuda de la siguiente salida:

Hello

starting my thread
Hello
Hello
Hello
Hello
ending my thread
Hello
Hello
Hello
Hello
Hello