Heim  >  Artikel  >  Backend-Entwicklung  >  Detaillierte Erklärung von Python-Coroutinen (mit Beispielen)

Detaillierte Erklärung von Python-Coroutinen (mit Beispielen)

不言
不言nach vorne
2018-10-08 16:22:275916Durchsuche

Dieser Artikel bietet Ihnen eine detaillierte Erklärung der Python-Coroutinen (mit Beispielen). Ich hoffe, dass er für Freunde in Not hilfreich ist.

Das Wechseln zwischen Prozessen und Threads dauert einige Zeit. Speichern Sie den aktuellen Status des Thread-Prozesses, um die Ausführung beim nächsten Mal fortzusetzen. In Programmen, die nicht viel CPU benötigen, also im Vergleich zu E/A-intensiven Programmen, verbrauchen Coroutinen weniger Ressourcen als Thread-Prozesse, wechseln schneller und eignen sich besser für E/A-intensive Programme. Die Coroutine ist ebenfalls Single-Threaded und kann die Vorteile der Multi-Core-CPU nicht nutzen. Wenn Sie die Vorteile der Multi-Core-CPU nutzen möchten, können Sie die Methode Prozess + Coroutine oder Prozess + Thread + Coroutine verwenden.

1. Einfache Implementierung von Coroutinen

Das Prinzip von Coroutinen wird durch Generatoren wie folgt implementiert: Das Programm wird bis Zeile 19 ausgeführt, führt die Verbraucherfunktion bis Zeile 13 aus, der nächste Generator führt den Produzenten aus Die Funktion stoppt in Zeile 8, kehrt zur Verbraucherfunktion in Zeile 13 zurück und setzt die Ausführung fort. Die Schleife erreicht erneut Zeile 13 und die Generatorfunktion wird an der Stelle ausgeführt, an der sie zuletzt gestoppt wurde. Wenn die Schleife auf diese Weise abgeschlossen ist, ist der Parallelitätseffekt abgeschlossen.

Aber einige Leute mögen sagen, dass das Ergebnis dasselbe ist, wenn ich eine Schleife verwende, um zwei Funktionen nacheinander auszuführen, wie im zweiten Beispiel unten gezeigt. Was ich hier sagen möchte, ist, dass dadurch die Ausführungsposition der Funktion nicht erhalten bleibt. Sie wechselt einfach zu einer anderen Funktion, wenn sie auf Vorgänge stößt, die ein Warten der CPU erfordern. Wenn Sie auf einen Vorgang stoßen, bei dem die CPU warten muss, geben Sie die CPU proaktiv auf, merken Sie sich die Ausführungsposition der Funktion und wechseln Sie zurück, um die Ausführung beim nächsten Mal fortzusetzen. Dies kann als gleichzeitiger Vorgang angesehen werden und den Parallelitätseffekt des Programms verbessern.

Coroutine implementiert einfach Produzenten und Konsumenten

import time
def producer():
    while True:
        time.sleep(1)
        print("+++++ 1个包子", time.strftime("%X"))
        yield
def consumer():
    while True:
        next(prd)
        print("----- 1个包子", time.strftime("%X"))
if __name__ == "__main__":
    prd = producer()
    consumer()
# 输出结果
+++++ 1个包子 16:22:30
----- 1个包子 16:22:30
+++++ 1个包子 16:22:31
----- 1个包子 16:22:31
+++++ 1个包子 16:22:32
----- 1个包子 16:22:32
+++++ 1个包子 16:22:33
----- 1个包子 16:22:33

Sequentieller Ausführungseffekt

import time
def producer():
    time.sleep(1)
    print("+++++ 1个包子", time.strftime("%X"))
def consumer():
    print("----- 1个包子", time.strftime("%X"))
if __name__ == "__main__":
    while True:
        producer()
        consumer()
# 输出结果
+++++ 1个包子 16:22:30
----- 1个包子 16:22:30
+++++ 1个包子 16:22:31
----- 1个包子 16:22:31
+++++ 1个包子 16:22:32
----- 1个包子 16:22:32
+++++ 1个包子 16:22:33
----- 1个包子 16:22:33

2. Greenlet

Das Greenlet-Modul muss installiert werden, pip install greenlet. Das Greenlet-Prinzip ist die Kapselung von Generatoren. Die Greenlet-Klasse stellt eine Methode bereit: switch: Wechseln Sie zur angegebenen Coroutine, wenn ein Wechsel erforderlich ist.

from greenlet import greenlet
import time
def producer():
    while True:
        time.sleep(1)
        print("+++++ 1个包子", time.strftime("%X"))
        gr2.switch()  # 切换到gr2运行
def consumer():
    while True:
        print("----- 1个包子", time.strftime("%X"))
        gr1.switch()  # 切换到gr1运行
if __name__ == "__main__":
    gr1 = greenlet(producer)
    gr2 = greenlet(consumer)
    gr1.switch()  # 切换到gr1运行
# 输出结果
+++++ 1个包子 09:39:45
----- 1个包子 09:39:45
+++++ 1个包子 09:39:46
----- 1个包子 09:39:46
+++++ 1个包子 09:39:47
----- 1个包子 09:39:47

3. gevent

Das gevent-Modul muss ebenfalls installiert werden, pip install gevent. Gevent ist eine Neukapselung von Gevent, die zeitaufwändige Vorgänge automatisch identifizieren und zu anderen Coroutinen wechseln kann. Beachten Sie, dass gevent die Coroutine nur dann umschaltet, wenn eine zeitaufwändige Operation auftritt. Es wird nicht aktiv umgeschaltet, wenn keine zeitaufwändige Operation auftritt.

gevent.spawn(*args, **kwargs) Der erste Parameter im Parameter variabler Länge ist die von der Coroutine ausgeführte Methode fn, und der Rest sind die Parameter von fn nacheinander. Rufen Sie nach dem Starten der Coroutine die Join-Methode auf.

Es gibt zwei Möglichkeiten, zeitaufwändige Vorgänge im Gevent-Modul zu identifizieren, ① Verwenden Sie überschriebene Klassen im gevent-Modul. Beispiel: gevent.socket gevent.sleep ② Patching-Methode, vor dem gesamten Code. aus gevent import Monkey importiert dieses Modul und Monkey.patch_all() ruft diese Methode auf.

Es wird empfohlen, die zweite Methode zu verwenden, damit der geschriebene Code nicht geändert werden muss

Unter normalen Umständen erkennt gevent keine zeitaufwändigen Vorgänge

import time
import gevent
def producer():
    for i in range(3):
        time.sleep(1)
        print("+++++ 1个包子", name, time.strftime("%X"))
def consumer():
    for i in range(3):
        time.sleep(1)
        print("----- 1个包子", name, time.strftime("%X"))
if __name__ == "__main__":
    g1 = gevent.spawn(producer, "zhangsan")
    g2 = gevent.spawn(consumer, "lisi")
    g1.join()
    g2.join()
# 输出结果
+++++ 1个包子 zhangsan 10:42:38
+++++ 1个包子 zhangsan 10:42:39
+++++ 1个包子 zhangsan 10:42:40
----- 1个包子 lisi 10:42:41
----- 1个包子 lisi 10:42:42
----- 1个包子 lisi 10:42:43

gevent identifiziert zeitaufwändige Betriebsmethode 1, verwenden Sie das Modul in gevent

import time
import gevent
def producer():
    for i in range(3):
        gevent.sleep(1)
        print("+++++ 1个包子", time.strftime("%X"))
def consumer():
    for i in range(3):
        gevent.sleep(1)
        print("----- 1个包子", time.strftime("%X"))
if __name__ == "__main__":
    g1 = gevent.spawn(producer)
    g2 = gevent.spawn(consumer)
    g1.join()
    g2.join()
# 输出结果
+++++ 1个包子 10:43:04
----- 1个包子 10:43:04
+++++ 1个包子 10:43:05
----- 1个包子 10:43:05
+++++ 1个包子 10:43:06
----- 1个包子 10:43:06

gevent identifiziert zeitaufwändige Betriebsmethode 2, Patch

import time
import gevent
from gevent import monkey
monkey.patch_all()
def producer():
    for i in range(3):
        time.sleep(1)
        print("+++++ 1个包子", time.strftime("%X"))
def consumer():
    for i in range(3):
        time.sleep(1)
        print("----- 1个包子", time.strftime("%X"))
if __name__ == "__main__":
    g1 = gevent.spawn(producer)
    g2 = gevent.spawn(consumer)
    g1.join()
    g2.join()
# 输出结果
+++++ 1个包子 10:44:04
----- 1个包子 10:44:04
+++++ 1个包子 10:44:05
----- 1个包子 10:44:05
+++++ 1个包子 10:44:06
----- 1个包子 10:44:06

Wenn viele offene Coroutinen vorhanden sind, ist es etwas mühsam, die Join-Methode einzeln aufzurufen. Daher stellt Gevent eine Methode „joinall()“ bereit, mit der alle Coroutinen gleichzeitig verbunden werden können. Die Methode joinall() übergibt eine Liste mit allen Coroutinen.

joinall

import time
import gevent
from gevent import monkey
monkey.patch_all()
def producer(name):
    for i in range(3):
        time.sleep(1)
        print("+++++ 1个包子", name, time.strftime("%X"))
def consumer(name):
    for i in range(3):
        time.sleep(1)
        print("----- 1个包子",  name, time.strftime("%X"))
if __name__ == "__main__":
    gevent.joinall([gevent.spawn(producer, "zhangsan"), gevent.spawn(consumer, "lisi")])
# 输出结果
+++++ 1个包子 zhangsan 10:51:34
----- 1个包子 lisi 10:51:34
+++++ 1个包子 zhangsan 10:51:35
----- 1个包子 lisi 10:51:35
+++++ 1个包子 zhangsan 10:51:36
----- 1个包子 lisi 10:51:36

4. Coroutine-Anwendung, gleichzeitiger Server

Der Server empfängt die Client-Nachricht und sendet sie so zurück, wie sie ist

Server

import socket
import gevent
from gevent import monkey
monkey.patch_all()
def fn(conn):
    msg = conn.recv(1024).decode("utf-8")
    print("服务的收到>>>", msg)
    conn.send(msg.encode("utf-8"))
sk = socket.socket()
sk.bind(("127.0.0.1", 8899))
sk.listen()
while True:
    conn, addr = sk.accept()
    print("已连接服务器-->", addr)
    gevent.spawn(fn, conn)
sk.close()
# 输出结果
已连接服务器--> ('127.0.0.1', 53878)
已连接服务器--> ('127.0.0.1', 53879)
已连接服务器--> ('127.0.0.1', 53880)
服务的收到>>> client1
服务的收到>>> client2
服务的收到>>> client3

Client

import socket
sk = socket.socket()
sk.connect(("127.0.0.1", 8899))
msg = input("客户端发送的内容>>> ")
sk.send(msg.encode("utf-8"))
msg = sk.recv(1024).decode("utf-8")
print("客户端收到>>>", msg)
sk.close()
# 输出结果
客户端发送的内容>>> client1
客户端收到>>> client1

Das obige ist der detaillierte Inhalt vonDetaillierte Erklärung von Python-Coroutinen (mit Beispielen). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:cnblogs.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen