¿Python soporta multiproceso? ¿Puede acelerar el tiempo de ejecución?

Estoy un poco confundido acerca de si el multiproceso funciona en Python o no.

Sé que ha habido muchas preguntas sobre esto y he leído muchas de ellas, pero todavía estoy confundido. Sé por experiencia propia y he visto a otros publicar sus propias respuestas y ejemplos aquí en StackOverflow que el multiproceso es posible en Python. Entonces, ¿por qué todos dicen que Python está bloqueado por la GIL y que solo un hilo puede ejecutarse a la vez? Claramente funciona. ¿O hay alguna distinción que no estoy llegando aquí?

Muchos carteles / encuestados también siguen mencionando que los hilos están limitados porque no utilizan múltiples núcleos. Pero diría que siguen siendo útiles porque funcionan simultáneamente y, por lo tanto, hacen que la carga de trabajo combinada se realice más rápido. Quiero decir, ¿por qué habría un módulo de hilo de Python?

Actualizar:

Gracias por todas las respuestas hasta el momento. La forma en que lo entiendo es que el subprocesamiento múltiple solo se ejecutará en paralelo para algunas tareas de E / S, pero solo puede ejecutarse de una en una para tareas principales múltiples vinculadas a la CPU.

No estoy del todo seguro de lo que esto significa para mí en términos prácticos, así que solo daré un ejemplo del tipo de tarea que me gustaría multiprocitar. Por ejemplo, digamos que quiero recorrer una larga lista de cadenas y quiero hacer algunas operaciones de cadena básicas en cada elemento de la lista. Si divido la lista, envío cada sub-lista para que sea procesada por mi código de bucle / cadena en un nuevo hilo, y envíe los resultados en una cola, ¿estas cargas de trabajo se ejecutarán aproximadamente al mismo tiempo? ¿Lo que es más importante, esto acelerará teóricamente el tiempo que lleva ejecutar el script?

Otro ejemplo podría ser si puedo renderizar y guardar cuatro imágenes diferentes usando PIL en cuatro hilos diferentes, ¿y esto es más rápido que procesar las imágenes una por una una después de la otra? Supongo que este componente de velocidad es lo que realmente me pregunto en lugar de cuál es la terminología correcta.

También conozco el módulo de multiprocesamiento, pero mi principal interés en este momento es para cargas de tareas pequeñas a medianas (10-30 segundos), por lo que creo que el multihilo será más apropiado porque los subprocesos pueden ser lentos para iniciar.

El GIL no impide el enhebrado. Todo lo que GIL hace es asegurarse de que solo un hilo esté ejecutando el código Python a la vez; El control todavía cambia entre hilos.

Lo que el GIL evita, entonces, es utilizar más de un núcleo de CPU o CPU separadas para ejecutar subprocesos en paralelo.

Esto solo se aplica al código de Python. Las extensiones C pueden y liberan GIL para permitir que múltiples subprocesos de código C y un subproceso de Python se ejecuten a través de múltiples núcleos. Esto se extiende a la E / S controlada por el kernel, como las llamadas select() para las lecturas y escrituras de socket, lo que hace que Python maneje los eventos de red de manera razonablemente eficiente en una configuración de múltiples núcleos de múltiples subprocesos.

Lo que hacen muchas implementaciones de servidores es ejecutar más de un proceso de Python, para permitir que el sistema operativo maneje la progtwigción entre procesos para utilizar sus núcleos de CPU al máximo. También puede usar la biblioteca de multiprocessing para manejar el parallel processing a través de múltiples procesos desde una base de código y un proceso principal, si eso se adapta a sus casos de uso.

Tenga en cuenta que GIL solo es aplicable a la implementación de CPython; Jython y IronPython utilizan una implementación de subprocesos diferente (los subprocesos de tiempo de ejecución comunes de Java VM y .NET nativos respectivamente).

Para abordar su actualización directamente: cualquier tarea que intente obtener un aumento de velocidad a partir de la ejecución paralela, utilizando el código puro de Python, no verá una aceleración, ya que el código de Python enlazado se bloquea a un hilo ejecutándose a la vez. Sin embargo, si combina las extensiones C y la E / S (como las operaciones PIL o numpy) y cualquier código C puede ejecutarse en paralelo con un subproceso de Python activo.

El subproceso de Python es excelente para crear una GUI sensible o para manejar múltiples solicitudes web cortas donde la E / S es el cuello de botella más que el código de Python. No es adecuado para paralelizar código de Python de uso intensivo de computación, atenerse al módulo de multiprocessing para tales tareas o delegar en una biblioteca externa dedicada.

Sí. 🙂

Usted tiene el módulo de subproceso de nivel bajo y el módulo de subprocesamiento de nivel superior. Pero si simplemente desea utilizar máquinas multinúcleo, el módulo de multiprocesamiento es el camino a seguir.

Cita de la documentación :

En CPython, debido al locking de intérprete global, solo un hilo puede ejecutar el código de Python a la vez (aunque ciertas bibliotecas orientadas al rendimiento pueden superar esta limitación). Si desea que su aplicación haga un mejor uso de los recursos computacionales de las máquinas con varios núcleos, se recomienda utilizar el multiprocesamiento. Sin embargo, el subproceso sigue siendo un modelo adecuado si desea ejecutar varias tareas enlazadas a E / S simultáneamente.

Python 3 tiene la facilidad de lanzar tareas paralelas . Esto facilita nuestro trabajo.

Tiene para agrupación de hilos y agrupación de procesos .

Lo siguiente da una idea:

Ejemplo de ThreadPoolExecutor

 import concurrent.futures import urllib.request URLS = ['http://www.foxnews.com/', 'http://www.cnn.com/', 'http://europe.wsj.com/', 'http://www.bbc.co.uk/', 'http://some-made-up-domain.com/'] # Retrieve a single page and report the URL and contents def load_url(url, timeout): with urllib.request.urlopen(url, timeout=timeout) as conn: return conn.read() # We can use a with statement to ensure threads are cleaned up promptly with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor: # Start the load operations and mark each future with its URL future_to_url = {executor.submit(load_url, url, 60): url for url in URLS} for future in concurrent.futures.as_completed(future_to_url): url = future_to_url[future] try: data = future.result() except Exception as exc: print('%r generated an exception: %s' % (url, exc)) else: print('%r page is %d bytes' % (url, len(data))) 

ProcessPoolExecutor

 import concurrent.futures import math PRIMES = [ 112272535095293, 112582705942171, 112272535095293, 115280095190773, 115797848077099, 1099726899285419] def is_prime(n): if n % 2 == 0: return False sqrt_n = int(math.floor(math.sqrt(n))) for i in range(3, sqrt_n + 1, 2): if n % i == 0: return False return True def main(): with concurrent.futures.ProcessPoolExecutor() as executor: for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)): print('%d is prime: %s' % (number, prime)) if __name__ == '__main__': main()