Maison  >  Article  >  développement back-end  >  Explication détaillée des coroutines Python (avec exemples)

Explication détaillée des coroutines Python (avec exemples)

不言
不言avant
2018-10-08 16:22:275898parcourir

Cet article vous apporte une explication détaillée des coroutines python (avec des exemples). Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer.

La commutation entre les processus et les threads prend du temps. Enregistrez l'état actuel du processus de thread pour continuer l'exécution la prochaine fois. Dans les programmes qui ne nécessitent pas beaucoup de CPU, c'est-à-dire par rapport aux programmes gourmands en E/S, les coroutines consomment moins de ressources que les processus de thread, commutent plus rapidement et conviennent mieux aux programmes gourmands en E/S. La coroutine est également monothread et ne peut pas profiter du processeur multicœur. Si vous souhaitez profiter du processeur multicœur, vous pouvez utiliser la méthode processus + coroutine, ou processus + thread + coroutine.

1. Implémentation simple des coroutines

Le principe des coroutines est implémenté via des générateurs, comme suit : le programme s'exécute jusqu'à la ligne 19, exécute la fonction consommateur jusqu'à la ligne 13, le générateur suivant, exécute le producteur La fonction s'arrête à la ligne 8, revient à la ligne de fonction consommateur 13 et continue l'exécution. La boucle atteint à nouveau la ligne 13 et la fonction génératrice s'exécutera à partir de l'endroit où le dernier rendement s'est arrêté. Lorsque la boucle est ainsi bouclée, l’effet de concurrence est terminé.

Mais certaines personnes peuvent dire que le résultat est le même si j'utilise une boucle pour exécuter deux fonctions en séquence, comme le montre le deuxième exemple ci-dessous. Ce que je veux dire ici, c'est que cela ne préserve pas la position d'exécution de la fonction. Il passe simplement à une autre fonction après l'exécution d'une fonction. Il ne peut pas basculer lorsqu'il rencontre des opérations qui nécessitent que le CPU attende. Lorsque vous rencontrez une opération qui nécessite que le CPU attende, abandonnez le CPU de manière proactive, mémorisez la position d'exécution de la fonction et revenez pour continuer l'exécution la prochaine fois, ce qui peut être considéré comme une opération simultanée et améliorer l'effet de concurrence du programme.

Les coroutines implémentent simplement les producteurs et les consommateurs

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

Effet d'exécution séquentielle

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

greenlet

Le module greenlet doit être installé, pip install greenlet. Le principe du greenlet est l'encapsulation des générateurs. La classe greenlet fournit une méthode, switch : bascule vers la coroutine spécifiée lorsqu'une commutation est requise.

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

Le module gevent doit également être installé, pip install gevent. gevent est une réencapsulation de gevent, qui peut identifier automatiquement les opérations fastidieuses et passer à d'autres coroutines. Notez que gevent ne changera la coroutine en cours d'exécution que lorsqu'il rencontrera une opération fastidieuse. Il ne changera pas activement s'il ne rencontre pas une opération fastidieuse.

gevent.spawn(*args, **kwargs) Le premier paramètre du paramètre de longueur variable est la méthode fn exécutée par la coroutine, et le reste sont les paramètres de fn en séquence. Après avoir démarré la coroutine, appelez la méthode join.

Il existe deux façons d'identifier les opérations chronophages dans le module gevent, ① Utilisez des classes remplacées dans le module gevent. Par exemple, gevent.socket gevent.sleep ② Méthode de correction, avant tout le code. depuis gevent import singe importe ce module et singe.patch_all() appelle cette méthode.

Il est recommandé d'utiliser la deuxième méthode, afin qu'il ne soit pas nécessaire de modifier le code qui a été écrit

Dans des circonstances normales, gevent ne reconnaîtra pas les opérations fastidieuses

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

méthode d'opération fastidieuse d'identification de gevent 1, utilisez le module dans 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

méthode d'opération fastidieuse d'identification de gevent 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

lorsqu'il est activé Lorsqu'il y a plusieurs coroutines, il est un peu gênant d'appeler la méthode join une par une, donc gevent fournit une méthode joinall(), qui peut joindre toutes les coroutines à la fois. La méthode joinall() transmet une liste contenant toutes les coroutines.

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. Application Coroutine, serveur concurrent

Le serveur reçoit le message client et le renvoie tel quel

serveur

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

clien

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

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer

Articles Liés

Voir plus