Me gustaría contribuir con un ejemplo simple y las explicaciones que he encontrado útiles cuando tuve que abordar este problema yo mismo.
En esta respuesta, encontrará información sobre el GIL de Python (bloqueo global del intérprete) y un ejemplo simple del día a día escrito usando multiprocessing.dummy más algunos puntos de referencia simples.
Bloqueo global de intérpretes (GIL)
Python no permite múltiples subprocesos en el verdadero sentido de la palabra. Tiene un paquete de subprocesos múltiples, pero si desea varios subprocesos para acelerar su código, generalmente no es una buena idea usarlo.
Python tiene una construcción llamada bloqueo global del intérprete (GIL). El GIL se asegura de que solo uno de sus 'hilos' pueda ejecutarse a la vez. Un hilo adquiere el GIL, hace un poco de trabajo, luego pasa el GIL al siguiente hilo.
Esto sucede muy rápido, por lo que para el ojo humano puede parecer que sus hilos se están ejecutando en paralelo, pero en realidad solo se turnan para usar el mismo núcleo de CPU.
Todo este paso de GIL agrega gastos generales a la ejecución. Esto significa que si desea que su código se ejecute más rápido, usar el paquete de subprocesos a menudo no es una buena idea.
Hay razones para usar el paquete de subprocesos de Python. Si desea ejecutar algunas cosas simultáneamente, y la eficiencia no es una preocupación, entonces está totalmente bien y conveniente. O si está ejecutando código que necesita esperar algo (como algunas E / S), entonces podría tener mucho sentido. Pero la biblioteca de subprocesos no le permitirá usar núcleos de CPU adicionales.
El subprocesamiento múltiple se puede subcontratar al sistema operativo (mediante el procesamiento múltiple) y a alguna aplicación externa que llame a su código de Python (por ejemplo, Spark o Hadoop ), o algún código al que llame su código de Python (por ejemplo: podría haga que su código de Python llame a una función C que hace el costoso material de múltiples subprocesos
Por qué esto importa
Porque muchas personas pasan mucho tiempo tratando de encontrar cuellos de botella en su elegante código multiproceso de Python antes de aprender qué es el GIL.
Una vez que esta información es clara, aquí está mi código:
#!/bin/python
from multiprocessing.dummy import Pool
from subprocess import PIPE,Popen
import time
import os
# In the variable pool_size we define the "parallelness".
# For CPU-bound tasks, it doesn't make sense to create more Pool processes
# than you have cores to run them on.
#
# On the other hand, if you are using I/O-bound tasks, it may make sense
# to create a quite a few more Pool processes than cores, since the processes
# will probably spend most their time blocked (waiting for I/O to complete).
pool_size = 8
def do_ping(ip):
if os.name == 'nt':
print ("Using Windows Ping to " + ip)
proc = Popen(['ping', ip], stdout=PIPE)
return proc.communicate()[0]
else:
print ("Using Linux / Unix Ping to " + ip)
proc = Popen(['ping', ip, '-c', '4'], stdout=PIPE)
return proc.communicate()[0]
os.system('cls' if os.name=='nt' else 'clear')
print ("Running using threads\n")
start_time = time.time()
pool = Pool(pool_size)
website_names = ["www.google.com","www.facebook.com","www.pinterest.com","www.microsoft.com"]
result = {}
for website_name in website_names:
result[website_name] = pool.apply_async(do_ping, args=(website_name,))
pool.close()
pool.join()
print ("\n--- Execution took {} seconds ---".format((time.time() - start_time)))
# Now we do the same without threading, just to compare time
print ("\nRunning NOT using threads\n")
start_time = time.time()
for website_name in website_names:
do_ping(website_name)
print ("\n--- Execution took {} seconds ---".format((time.time() - start_time)))
# Here's one way to print the final output from the threads
output = {}
for key, value in result.items():
output[key] = value.get()
print ("\nOutput aggregated in a Dictionary:")
print (output)
print ("\n")
print ("\nPretty printed output: ")
for key, value in output.items():
print (key + "\n")
print (value)