Buscar..


Observaciones

Los desarrolladores de Python se aseguraron de que la API entre threading y multiprocessing sea ​​similar, de modo que el cambio entre las dos variantes sea más fácil para los programadores.

El módulo de enhebrado.

from __future__ import print_function
import threading
def counter(count):
    while count > 0:
        print("Count value", count)
        count -= 1
    return

t1 = threading.Thread(target=countdown,args=(10,))
t1.start()
t2 = threading.Thread(target=countdown,args=(20,))
t2.start()

En ciertas implementaciones de Python como CPython, cierto paralelismo no se consigue utilizando hilos porque de la utilización de lo que se conoce como el GIL, o G lobal I nterpreter L ock.

Aquí hay una excelente descripción de la concurrencia de Python:

Concordancia Python por David Beazley (YouTube)

El módulo multiprocesamiento.

from __future__ import print_function
import multiprocessing


def countdown(count):
    while count > 0:
        print("Count value", count)
        count -= 1
    return

if __name__ == "__main__":
    p1 = multiprocessing.Process(target=countdown, args=(10,))
    p1.start()

    p2 = multiprocessing.Process(target=countdown, args=(20,))
    p2.start()

    p1.join()
    p2.join()

Aquí, cada función se ejecuta en un nuevo proceso. Dado que una nueva instancia de Python VM ejecuta el código, no hay GIL y se ejecuta el paralelismo en varios núcleos.

El método Process.start inicia este nuevo proceso y ejecuta la función pasada en el argumento de target con los argumentos args . El método Process.join espera el final de la ejecución de los procesos p1 y p2 .

Los nuevos procesos se inician de manera diferente según la versión de python y la plataforma en la que se ejecuta el código, por ejemplo :

  • Windows usa spawn para crear el nuevo proceso.
  • Con los sistemas y versiones de Unix anteriores a 3.3, los procesos se crean utilizando un fork .
    Tenga en cuenta que este método no respeta el uso POSIX de la bifurcación y, por lo tanto, conduce a comportamientos inesperados, especialmente al interactuar con otras bibliotecas de multiprocesamiento.
  • Con el sistema Unix y la versión 3.4+, puede elegir iniciar los nuevos procesos con fork , forkserver o spawn utilizando multiprocessing.set_start_method al comienzo de su programa. forkserver métodos forkserver y spawn son más lentos que los forking, pero evitan algunos comportamientos inesperados.

Uso de la horquilla POSIX :

Después de una bifurcación en un programa multiproceso, el niño solo puede llamar de forma segura a funciones async-signal-safe hasta el momento en que lo llame execve.
( ver )

Usando fork, se iniciará un nuevo proceso con el mismo estado exacto para todo el mutex actual, pero solo se MainThread . Esto no es seguro ya que podría conducir a condiciones de carrera, por ejemplo :

  • Si utiliza un Lock en MainThread y lo pasa a otro hilo que se supone que debe bloquearlo en algún momento. Si la fork ocurre simultáneamente, el nuevo proceso comenzará con un bloqueo bloqueado que nunca se liberará ya que el segundo hilo no existe en este nuevo proceso.

En realidad, este tipo de comportamiento no debería ocurrir en Python puro, ya que el multiprocessing maneja correctamente, pero si está interactuando con otra biblioteca, puede ocurrir este tipo de comportamiento, lo que puede provocar un fallo en su sistema (por ejemplo, con numpy / acelerado en macOS)

Transferencia de datos entre procesos de multiprocesamiento.

Debido a que los datos son confidenciales cuando se manejan entre dos subprocesos (piense que la lectura concurrente y la escritura concurrente pueden entrar en conflicto entre sí, causando condiciones de carrera), se creó un conjunto de objetos únicos para facilitar la transferencia de datos entre los subprocesos. Cualquier operación verdaderamente atómica puede usarse entre subprocesos, pero siempre es seguro mantener la cola.

import multiprocessing
import queue
my_Queue=multiprocessing.Queue() 
#Creates a queue with an undefined maximum size
#this can be dangerous as the queue becomes increasingly large
#it will take a long time to copy data to/from each read/write thread

La mayoría de las personas sugerirán que al usar la cola, siempre coloque los datos de la cola en un intento: excepto: bloque en lugar de usar vacío. Sin embargo, para las aplicaciones en las que no importa si omite un ciclo de exploración (los datos se pueden colocar en la cola mientras se queue.Empty==True estados de la queue.Empty==True a la queue.Empty==False ) por lo general es mejor colocar la lectura y acceso de escritura en lo que yo llamo un bloque Iftry, porque una declaración 'if' es técnicamente más eficaz que atrapar la excepción.

import multiprocessing
import queue
'''Import necessary Python standard libraries, multiprocessing for classes and queue for the queue exceptions it provides'''
def Queue_Iftry_Get(get_queue, default=None, use_default=False, func=None, use_func=False):
    '''This global method for the Iftry block is provided for it's reuse and 
standard functionality, the if also saves on performance as opposed to catching
 the exception, which is expencive.
        It also allows the user to specify a function for the outgoing data to use,
 and a default value to return if the function cannot return the value from the queue'''
        if get_queue.empty():
            if use_default:
                return default
        else:
            try:
                value = get_queue.get_nowait()
            except queue.Empty:
                if use_default:
                    return default
            else:
                if use_func:
                    return func(value)
                else:
                    return value
    def Queue_Iftry_Put(put_queue, value):
        '''This global method for the Iftry block is provided because of its reuse 
and 
standard functionality, the If also saves on performance as opposed to catching
 the exception, which is expensive.
        Return True if placing value in the queue was successful. Otherwise, false'''
        if put_queue.full():
            return False
        else:
            try:
                put_queue.put_nowait(value)
            except queue.Full:
                return False
            else:
                return True


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow