Maison > Article > développement back-end > Explication détaillée des coroutines Python (avec exemples)
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!