Python 3 - Programación multiproceso
Ejecutar varios subprocesos es similar a ejecutar varios programas diferentes al mismo tiempo, pero con los siguientes beneficios:
Varios subprocesos dentro de un proceso comparten el mismo espacio de datos con el subproceso principal y, por lo tanto, pueden compartir información o comunicarse entre sí más fácilmente que si fueran procesos separados.
Los subprocesos a veces se denominan procesos ligeros y no requieren mucha sobrecarga de memoria; son más baratos que los procesos.
Un hilo tiene un comienzo, una secuencia de ejecución y una conclusión. Tiene un puntero de instrucción que realiza un seguimiento de dónde se está ejecutando actualmente dentro de su contexto.
Se puede adelantar (interrumpir).
Se puede poner en espera temporalmente (también conocido como inactivo) mientras se ejecutan otros subprocesos; esto se llama rendimiento.
Hay dos tipos diferentes de hilos:
- hilo de kernel
- hilo de usuario
Los subprocesos del kernel son parte del sistema operativo, mientras que los subprocesos del espacio de usuario no se implementan en el kernel.
Hay dos módulos que admiten el uso de subprocesos en Python3:
- _thread
- threading
El módulo de subprocesos ha estado "en desuso" durante bastante tiempo. Se anima a los usuarios a utilizar el módulo de subprocesos en su lugar. Por lo tanto, en Python 3, el módulo "hilo" ya no está disponible. Sin embargo, ha sido renombrado a "_thread" para compatibilidades con versiones anteriores en Python3.
Comenzando un nuevo hilo
Para generar otro hilo, debe llamar al siguiente método disponible en el módulo del hilo :
_thread.start_new_thread ( function, args[, kwargs] )
Esta llamada al método permite una forma rápida y eficiente de crear nuevos subprocesos tanto en Linux como en Windows.
La llamada al método regresa inmediatamente y el subproceso secundario comienza y llama a la función con la lista pasada de argumentos . Cuando la función regresa, el hilo termina.
Aquí, args es una tupla de argumentos; use una tupla vacía para llamar a la función sin pasar ningún argumento. kwargs es un diccionario opcional de argumentos de palabras clave.
Ejemplo
#!/usr/bin/python3
import _thread
import time
# Define a function for the thread
def print_time( threadName, delay):
count = 0
while count < 5:
time.sleep(delay)
count += 1
print ("%s: %s" % ( threadName, time.ctime(time.time()) ))
# Create two threads as follows
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
Cuando se ejecuta el código anterior, produce el siguiente resultado:
Thread-1: Fri Feb 19 09:41:39 2016
Thread-2: Fri Feb 19 09:41:41 2016
Thread-1: Fri Feb 19 09:41:41 2016
Thread-1: Fri Feb 19 09:41:43 2016
Thread-2: Fri Feb 19 09:41:45 2016
Thread-1: Fri Feb 19 09:41:45 2016
Thread-1: Fri Feb 19 09:41:47 2016
Thread-2: Fri Feb 19 09:41:49 2016
Thread-2: Fri Feb 19 09:41:53 2016
El programa va en un bucle infinito. Tendrás que presionar ctrl-c para detener
Aunque es muy efectivo para el subproceso de bajo nivel, el módulo de subprocesos es muy limitado en comparación con el módulo de subprocesamiento más nuevo.
El módulo de subprocesamiento
El módulo de subprocesos más nuevo incluido con Python 2.4 proporciona un soporte de alto nivel mucho más poderoso para subprocesos que el módulo de subprocesos discutido en la sección anterior.
El módulo de subprocesos expone todos los métodos del módulo de subprocesos y proporciona algunos métodos adicionales:
threading.activeCount() - Devuelve el número de objetos de hilo que están activos.
threading.currentThread() - Devuelve el número de objetos de hilo en el control de hilo de la persona que llama.
threading.enumerate() - Devuelve una lista de todos los objetos de hilo que están actualmente activos.
Además de los métodos, el módulo de subprocesos tiene la clase Thread que implementa subprocesos. Los métodos proporcionados por la clase Thread son los siguientes:
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.
Creación de subprocesos mediante el módulo de subprocesos
Para implementar un nuevo hilo usando el módulo de subprocesos, debe hacer lo siguiente:
Defina una nueva subclase de la clase Thread .
Anule el método __init __ (self [, args]) para agregar argumentos adicionales.
Luego, anule el método run (self [, args]) para implementar lo que debería hacer el hilo cuando se inicia.
Una vez que haya creado la nueva subclase de Thread , puede crear una instancia de ella y luego iniciar un nuevo hilo invocando start () , que a su vez llama al método run () .
Ejemplo
#!/usr/bin/python3
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
# Create new threads
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
# Start new Threads
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print ("Exiting Main Thread")
Resultado
Cuando ejecutamos el programa anterior, produce el siguiente resultado:
Starting Thread-1
Starting Thread-2
Thread-1: Fri Feb 19 10:00:21 2016
Thread-2: Fri Feb 19 10:00:22 2016
Thread-1: Fri Feb 19 10:00:22 2016
Thread-1: Fri Feb 19 10:00:23 2016
Thread-2: Fri Feb 19 10:00:24 2016
Thread-1: Fri Feb 19 10:00:24 2016
Thread-1: Fri Feb 19 10:00:25 2016
Exiting Thread-1
Thread-2: Fri Feb 19 10:00:26 2016
Thread-2: Fri Feb 19 10:00:28 2016
Thread-2: Fri Feb 19 10:00:30 2016
Exiting Thread-2
Exiting Main Thread
Sincronizar hilos
El módulo de subprocesos proporcionado con Python incluye un mecanismo de bloqueo fácil de implementar que le permite sincronizar subprocesos. Se crea un nuevo bloqueo llamando al método Lock () , que devuelve el nuevo bloqueo.
El método de adquisición (bloqueo) del nuevo objeto de bloqueo se utiliza para forzar que los subprocesos se ejecuten sincrónicamente. El parámetro de bloqueo opcional le permite controlar si el hilo espera para adquirir el bloqueo.
Si el bloqueo se establece en 0, el hilo regresa inmediatamente con un valor de 0 si el bloqueo no se puede adquirir y con un 1 si se adquirió el bloqueo. Si el bloqueo se establece en 1, el hilo se bloquea y espera a que se libere el bloqueo.
El método release () del nuevo objeto de bloqueo se utiliza para liberar el bloqueo cuando ya no es necesario.
Ejemplo
#!/usr/bin/python3
import threading
import time
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)
# Get lock to synchronize threads
threadLock.acquire()
print_time(self.name, self.counter, 3)
# Free lock to release next thread
threadLock.release()
def print_time(threadName, delay, counter):
while counter:
time.sleep(delay)
print ("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
threadLock = threading.Lock()
threads = []
# Create new threads
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
# Start new Threads
thread1.start()
thread2.start()
# Add threads to thread list
threads.append(thread1)
threads.append(thread2)
# Wait for all threads to complete
for t in threads:
t.join()
print ("Exiting Main Thread")
Salida
Cuando se ejecuta el código anterior, produce el siguiente resultado:
Starting Thread-1
Starting Thread-2
Thread-1: Fri Feb 19 10:04:14 2016
Thread-1: Fri Feb 19 10:04:15 2016
Thread-1: Fri Feb 19 10:04:16 2016
Thread-2: Fri Feb 19 10:04:18 2016
Thread-2: Fri Feb 19 10:04:20 2016
Thread-2: Fri Feb 19 10:04:22 2016
Exiting Main Thread
Cola de prioridad multiproceso
El módulo de cola le permite crear un nuevo objeto de cola que puede contener un número específico de elementos. Existen los siguientes métodos para controlar la cola:
get() - El get () elimina y devuelve un elemento de la cola.
put() - El puesto agrega el artículo a una cola.
qsize() - El qsize () devuelve el número de elementos que están actualmente en la cola.
empty()- El vacío () devuelve Verdadero si la cola está vacía; de lo contrario, Falso.
full()- full () devuelve True si la cola está llena; de lo contrario, Falso.
Ejemplo
#!/usr/bin/python3
import queue
import threading
import time
exitFlag = 0
class myThread (threading.Thread):
def __init__(self, threadID, name, q):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.q = q
def run(self):
print ("Starting " + self.name)
process_data(self.name, self.q)
print ("Exiting " + self.name)
def process_data(threadName, q):
while not exitFlag:
queueLock.acquire()
if not workQueue.empty():
data = q.get()
queueLock.release()
print ("%s processing %s" % (threadName, data))
else:
queueLock.release()
time.sleep(1)
threadList = ["Thread-1", "Thread-2", "Thread-3"]
nameList = ["One", "Two", "Three", "Four", "Five"]
queueLock = threading.Lock()
workQueue = queue.Queue(10)
threads = []
threadID = 1
# Create new threads
for tName in threadList:
thread = myThread(threadID, tName, workQueue)
thread.start()
threads.append(thread)
threadID += 1
# Fill the queue
queueLock.acquire()
for word in nameList:
workQueue.put(word)
queueLock.release()
# Wait for queue to empty
while not workQueue.empty():
pass
# Notify threads it's time to exit
exitFlag = 1
# Wait for all threads to complete
for t in threads:
t.join()
print ("Exiting Main Thread")
Salida
Cuando se ejecuta el código anterior, produce el siguiente resultado:
Starting Thread-1
Starting Thread-2
Starting Thread-3
Thread-1 processing One
Thread-2 processing Two
Thread-3 processing Three
Thread-1 processing Four
Thread-2 processing Five
Exiting Thread-3
Exiting Thread-1
Exiting Thread-2
Exiting Main Thread