¿Cómo afecta “GIL” a Python asyncio `run_in_executor` con tareas vinculadas a E/S?
En relación a este código ejemplo de Python asyncio
run_in_executor
:
import asyncio
import concurrent.futures
def blocking_io():
# Las operaciones de archivo (como el registro) pueden bloquear
# el bucle de eventos: ejecútalas en un grupo de subprocesos.
with open('/dev/urandom', 'rb') as f:
return f.read(100)
def cpu_bound():
# Las operaciones intensivas en CPU bloquearán el bucle de eventos:
# en general, es preferible ejecutarlas en un grupo de procesos.
return sum(i * i for i in range(10 ** 7))
async def main():
loop = asyncio.get_running_loop()
## Opciones:
# 1. Ejecutar en el grupo de subprocesos predeterminado del bucle:
result = await loop.run_in_executor(
None, blocking_io)
print('Grupo de subprocesos predeterminado:', result)
# 3. Ejecutar en un grupo de procesos personalizado:
with concurrent.futures.ProcessPoolExecutor() as pool:
result = await loop.run_in_executor(
pool, cpu_bound)
print('Grupo de procesos personalizado:', result)
asyncio.run(main())
El ejemplo (en comentarios) recomienda ejecutar la función de E/S enlazada utilizando el ThreadPoolExecutor
y la función de CPU enlazada utilizando el ProcessPoolExecutor
. Quiero verificar mi comprensión de las razones detrás de esto con tres preguntas:
-
Estas recomendaciones no son realmente recomendaciones, ya que de lo contrario el bucle de eventos se bloqueará. En consecuencia, perderemos el beneficio principal de la programación de eventos, ¿correcto?
-
Ejecutar la tarea de enlace de E/S en un hilo separado requiere la siguiente suposición: ¿La llamada de E/S liberará el GIL? De lo contrario, el sistema operativo no podrá cambiar de contexto entre el bucle de eventos y este nuevo hilo separado.
-
Si la respuesta al punto 2 es sí, ¿cómo saber con certeza si una llamada de E/S libera el GIL o no?
davy.ai