Aprendizaje de Python - coroutine

Coroutine, también conocido como microhilo, fibra. El nombre en inglés es Coroutine.

El concepto de corrutinas se propuso muy temprano, pero no se ha utilizado ampliamente en ciertos lenguajes (como Lua) hasta los últimos años.

Las subrutinas o funciones se denominan jerárquicamente en todos los lenguajes, por ejemplo, A llama a B, B llama a C durante la ejecución, C vuelve después de la ejecución, B vuelve después de la ejecución y finalmente A se ejecuta.

Entonces, la llamada a la subrutina se realiza a través de la pila, y un hilo debe ejecutar una subrutina.

Una llamada de subrutina es siempre una entrada, un retorno y la secuencia de llamada es clara. La llamada de una corrutina es diferente de una subrutina.

La corrutina parece ser una subrutina, pero en el proceso de ejecución, se puede interrumpir dentro de la subrutina y luego cambiar para ejecutar otras subrutinas, y luego regresar para continuar la ejecución cuando sea apropiado.

Tenga en cuenta que interrumpir en una subrutina para ejecutar otras subrutinas no es una llamada de función, pero es algo similar a las interrupciones de la CPU . Por ejemplo, subrutina A, B:

def A():
    print('1')
    print('2')
    print('3')

def B():
    print('x')
    print('y')
    print('z')

Suponiendo que es ejecutado por una corrutina, durante la ejecución de A, se puede interrumpir en cualquier momento para ejecutar B. B también se puede interrumpir durante la ejecución y luego ejecutar A. El resultado puede ser:

1
2
x
y
3
z

Pero en A, B no se llama, por lo que la llamada a la corrutina es más difícil de entender que la llamada a la función.

Parece que la ejecución de A y B es un poco como multi-threading, pero la característica de la coroutine es que es ejecutada por un hilo. Comparado con multi-threading, ¿cuáles son las ventajas de la coroutine?

La mayor ventaja es la eficiencia de ejecución extremadamente alta de la corrutina . Debido a que la conmutación de subrutinas no es conmutación de subprocesos, está controlada por el programa mismo. Por lo tanto, no hay una sobrecarga de conmutación de subprocesos . En comparación con el subproceso múltiple, cuantos más subprocesos haya, más obvia será la ventaja de rendimiento de la corrutina.

La segunda gran ventaja es que no hay necesidad de un mecanismo de bloqueo de subprocesos múltiples , porque solo hay un subproceso y no hay conflicto de escritura de variables al mismo tiempo. En la corrutina, los recursos compartidos se controlan sin bloqueo, y solo se juzga el estado, por lo que la eficiencia de ejecución es mucho mayor, el hilo es mucho mayor .

Debido a que la corrutina es ejecutada por un hilo, ¿cómo usar una CPU de múltiples núcleos? El método más simple es multiproceso + corrutina , que no solo hace un uso completo de múltiples núcleos, sino que también da pleno juego a la alta eficiencia de la corrutina para obtener un rendimiento extremadamente alto.

El soporte de Python para corrutinas se logra a través de generadores.

(Dije cómo continuar la ejecución desde la posición de rendimiento anterior cuando se ejecute la próxima vez, resultó ser una corrutina)

En el generador, no solo podemos iterar a través del ciclo for, sino también obtener continuamente el siguiente valor devuelto por la declaración de rendimiento a través de la función next ().

Pero el rendimiento de Python no solo puede devolver un valor, también puede recibir parámetros enviados por la persona que llama.

Mira el ejemplo:

El modelo tradicional productor-consumidor es que un hilo escribe mensajes, un hilo recupera mensajes y controla la cola y la espera a través del mecanismo de bloqueo, pero puede bloquearse si no se tiene cuidado.

Si cambia a una corrutina, después de que el productor genera un mensaje, salta directamente al consumidor a través del rendimiento para iniciar la ejecución. Después de que se ejecuta el consumidor, el productor continuará produciendo el cambio, lo cual es extremadamente eficiente:

def consumer():
	r = ''
	while True:
		n = yield r
		if not n:
			return
		print('[CONSUMER] Consuming %s...' % n)
		r = '200 OK'
	
def produce(c):
	c.send(None)
	n = 0
	while n < 5:
		n = n + 1
		print('[PRODUCER] Producing %s...' % n)
        r = c.send(n)
        print('[PRODUCER] Consumer return: %s' % r)
    c.close()

c = consumer()
produce(c)

Resultados de:

[PRODUCER] Producing 1...
[CONSUMER] Consuming 1...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 2...
[CONSUMER] Consuming 2...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 3...
[CONSUMER] Consuming 3...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 4...
[CONSUMER] Consuming 4...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 5...
[CONSUMER] Consuming 5...
[PRODUCER] Consumer return: 200 OK

Tenga en cuenta que la función de consumidor es un generador. Después de pasar un consumidor a producir:

  1. Primero llame a c.send (None) para arrancar el generador;
  2. Luego, una vez que se produce algo, cambie a la ejecución del consumidor a través de c.send (n);
  3. El consumidor recibe el mensaje a través del rendimiento, lo procesa y envía el resultado a través del rendimiento;
  4. Produce obtiene el resultado del procesamiento del consumidor y continúa generando el siguiente mensaje;
  5. Produce decide no producir, cierra al consumidor a través de c.close () y termina todo el proceso.

Todo el proceso está libre de bloqueos y es ejecutado por un hilo. El productor y el consumidor cooperan para completar la tarea, por lo que se llama "corrutina" en lugar de multitarea preventiva del hilo.

(Esto es lo que hace el generador)

Finalmente, utilizo la oración de Donald Knuth para resumir las características de la corrutina:

"Una subrutina es un caso especial de una corrutina".

Supongo que te gusta

Origin blog.csdn.net/qq_44787943/article/details/112642111
Recomendado
Clasificación