iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >python 协程
  • 937
分享到

python 协程

python协程 2023-01-30 22:01:39 937人浏览 独家记忆

Python 官方文档:入门教程 => 点击学习

摘要

进程和线程都会的切换都要消耗时间,保存线程进程当前状态以便下次继续执行。在不怎么需要cpu的程序中,即相对于io密集型的程序,协程相对于线程进程资源消耗更小,切换更快,更适用于IO密集型。协程也是单线程的,没法利用cpu的多核,想利用cp

进程和线程都会的切换都要消耗时间,保存线程进程当前状态以便下次继续执行。在不怎么需要cpu的程序中,即相对于io密集型的程序,协程相对于线程进程资源消耗更小,切换更快,更适用于IO密集型。协程也是单线程的,没法利用cpu的多核,想利用cpu多核可以通过,进程+协程的方式,又或者进程+线程+协程。

 

1、协程的简单实现

协程的原理是通过生成器实现,如下:程序执行到19行,执行consumer函数到13行,next生成器,执行producer函数到8行停下,返回consumer函数13行继续往下执行,循环一次再次来到13行,生成器函数将从上次yield停下的地方往下执行。循环如此就完成就完成了并发的效果。

但有人可能会说我用循环顺序执行两个函数结果也一样啊,如下第2例子。当这里想说的是,这样并不能保留函数的执行的位置,只是简单的一个函数执行结束换到另一个函数而已,遇到需要cpu等待的操作也没法切换。在遇到需要cpu等待的操作主动让出cpu,记住函数执行的位置,下次切换回来继续执行才能算是并发的运行,提高程序的并发效果。

 1 import time
 2 
 3 
 4 def producer():
 5     while True:
 6         time.sleep(1)
 7         print("+++++ 1个包子", time.strftime("%X"))
 8         yield
 9 
10 
11 def consumer():
12     while True:
13         next(prd)
14         print("----- 1个包子", time.strftime("%X"))
15 
16 
17 if __name__ == "__main__":
18     prd = producer()
19     consumer()
20 
21 
22 # 输出结果
23 +++++ 1个包子 16:22:30
24 ----- 1个包子 16:22:30
25 +++++ 1个包子 16:22:31
26 ----- 1个包子 16:22:31
27 +++++ 1个包子 16:22:32
28 ----- 1个包子 16:22:32
29 +++++ 1个包子 16:22:33
30 ----- 1个包子 16:22:33
协程简单实现生产者与消费者
 1 import time
 2 
 3 
 4 def producer():
 5     time.sleep(1)
 6     print("+++++ 1个包子", time.strftime("%X"))
 7 
 8 
 9 def consumer():
10     print("----- 1个包子", time.strftime("%X"))
11 
12 
13 if __name__ == "__main__":
14     while True:
15         producer()
16         consumer()
17 
18 
19 # 输出结果
20 +++++ 1个包子 16:22:30
21 ----- 1个包子 16:22:30
22 +++++ 1个包子 16:22:31
23 ----- 1个包子 16:22:31
24 +++++ 1个包子 16:22:32
25 ----- 1个包子 16:22:32
26 +++++ 1个包子 16:22:33
27 ----- 1个包子 16:22:33
顺序执行效果

 

2、greenlet

greenlet模块需要安装,pip install greenlet。greenlet原理是对生成器的封装。greenlet类提供了一个方法,switch:在需要进行切换的时候切换到指定的协程。

 1 from greenlet import greenlet
 2 import time
 3 
 4 
 5 def producer():
 6     while True:
 7         time.sleep(1)
 8         print("+++++ 1个包子", time.strftime("%X"))
 9         gr2.switch()  # 切换到gr2运行
10 
11 
12 
13 def consumer():
14     while True:
15         print("----- 1个包子", time.strftime("%X"))
16         gr1.switch()  # 切换到gr1运行
17 
18 
19 if __name__ == "__main__":
20     gr1 = greenlet(producer)
21     gr2 = greenlet(consumer)
22     gr1.switch()  # 切换到gr1运行
23 
24 
25 # 输出结果
26 +++++ 1个包子 09:39:45
27 ----- 1个包子 09:39:45
28 +++++ 1个包子 09:39:46
29 ----- 1个包子 09:39:46
30 +++++ 1个包子 09:39:47
31 ----- 1个包子 09:39:47
greenlet简单实现

 

3、gevent

gevent模块也需要安装,pip install gevent。gevent是对gevent的再次封装,能自动识别耗时操作切换到其它协程。注意gevent遇到耗时操作才会切换协程运行,没有遇到耗时操作是不会主动切换的。

gevent.spawn(*args, **kwargs)    不定长参数中的第一个参数为协程执行的方法fn,其余的依次为 fn 的参数。开启了协程后要调用join方法。

gevent模块中识别耗时的操作有两种方式,① 使用gevent模块中重写的类。如,gevent.Socket  gevent.sleep  ② 打补丁的方式,在所有的代码前。from gevent import  monkey 导入这个模块,monkey.patch_all()调用这个方法。

推荐使用第二种方式,这样就不用更改已经写好的代码

 1 import time
 2 import gevent
 3 
 4 
 5 def producer():
 6     for i in range(3):
 7         time.sleep(1)
 8         print("+++++ 1个包子", name, time.strftime("%X"))
 9 
10 
11 def consumer():
12     for i in range(3):
13         time.sleep(1)
14         print("----- 1个包子", name, time.strftime("%X"))
15 
16 
17 if __name__ == "__main__":
18     g1 = gevent.spawn(producer, "zhangsan")
19     g2 = gevent.spawn(consumer, "lisi")
20     g1.join()
21     g2.join()
22 
23 
24 # 输出结果
25 +++++ 1个包子 zhangsan 10:42:38
26 +++++ 1个包子 zhangsan 10:42:39
27 +++++ 1个包子 zhangsan 10:42:40
28 ----- 1个包子 lisi 10:42:41
29 ----- 1个包子 lisi 10:42:42
30 ----- 1个包子 lisi 10:42:43
正常情况下gevent并不会识别耗时操作
 1 import time
 2 import gevent
 3 
 4 
 5 def producer():
 6     for i in range(3):
 7         gevent.sleep(1)
 8         print("+++++ 1个包子", time.strftime("%X"))
 9 
10 
11 def consumer():
12     for i in range(3):
13         gevent.sleep(1)
14         print("----- 1个包子", time.strftime("%X"))
15 
16 
17 if __name__ == "__main__":
18     g1 = gevent.spawn(producer)
19     g2 = gevent.spawn(consumer)
20     g1.join()
21     g2.join()
22 
23 
24 # 输出结果
25 +++++ 1个包子 10:43:04
26 ----- 1个包子 10:43:04
27 +++++ 1个包子 10:43:05
28 ----- 1个包子 10:43:05
29 +++++ 1个包子 10:43:06
30 ----- 1个包子 10:43:06
gevent识别耗时操作方式1,使用gevent中的模块
 1 import time
 2 import gevent
 3 from gevent import monkey
 4 monkey.patch_all()
 5 
 6 
 7 def producer():
 8     for i in range(3):
 9         time.sleep(1)
10         print("+++++ 1个包子", time.strftime("%X"))
11 
12 
13 def consumer():
14     for i in range(3):
15         time.sleep(1)
16         print("----- 1个包子", time.strftime("%X"))
17 
18 
19 if __name__ == "__main__":
20     g1 = gevent.spawn(producer)
21     g2 = gevent.spawn(consumer)
22     g1.join()
23     g2.join()
24 
25 
26 # 输出结果
27 +++++ 1个包子 10:44:04
28 ----- 1个包子 10:44:04
29 +++++ 1个包子 10:44:05
30 ----- 1个包子 10:44:05
31 +++++ 1个包子 10:44:06
32 ----- 1个包子 10:44:06
gevent识别耗时操作方式2,打补丁

当开启的协程很多的时候,一个个的调用join方法就有点麻烦,所以gevent提供了一个方法joinall(),可以一次join所有的协程。joinall() 方法传参一个列表,列表包含了所有的协程。

 1 import time
 2 import gevent
 3 from gevent import monkey
 4 monkey.patch_all()
 5 
 6 
 7 def producer(name):
 8     for i in range(3):
 9         time.sleep(1)
10         print("+++++ 1个包子", name, time.strftime("%X"))
11 
12 
13 def consumer(name):
14     for i in range(3):
15         time.sleep(1)
16         print("----- 1个包子",  name, time.strftime("%X"))
17 
18 
19 if __name__ == "__main__":
20     gevent.joinall([gevent.spawn(producer, "zhangsan"), gevent.spawn(consumer, "lisi")])
21 
22 
23 # 输出结果
24 +++++ 1个包子 zhangsan 10:51:34
25 ----- 1个包子 lisi 10:51:34
26 +++++ 1个包子 zhangsan 10:51:35
27 ----- 1个包子 lisi 10:51:35
28 +++++ 1个包子 zhangsan 10:51:36
29 ----- 1个包子 lisi 10:51:36
joinall

 

4、协程应用,并发服务器

服务端收到客户端消息,并原样发送回去

 1 import socket
 2 import gevent
 3 from gevent import monkey
 4 
 5 monkey.patch_all()
 6 
 7 
 8 def fn(conn):
 9     msg = conn.recv(1024).decode("utf-8")
10     print("服务的收到>>>", msg)
11     conn.send(msg.encode("utf-8"))
12 
13 
14 sk = socket.socket()
15 sk.bind(("127.0.0.1", 8899))
16 sk.listen()
17 while True:
18     conn, addr = sk.accept()
19     print("已连接服务器-->", addr)
20     gevent.spawn(fn, conn)
21 sk.close()
22 
23 
24 # 输出结果
25 已连接服务器--> ('127.0.0.1', 53878)
26 已连接服务器--> ('127.0.0.1', 53879)
27 已连接服务器--> ('127.0.0.1', 53880)
28 服务的收到>>> client1
29 服务的收到>>> client2
30 服务的收到>>> client3
server
 1 import socket
 2 
 3 sk = socket.socket()
 4 sk.connect(("127.0.0.1", 8899))
 5 msg = input("客户端发送的内容>>> ")
 6 sk.send(msg.encode("utf-8"))
 7 msg = sk.recv(1024).decode("utf-8")
 8 print("客户端收到>>>", msg)
 9 sk.close()
10 
11 
12 # 输出结果
13 客户端发送的内容>>> client1
14 客户端收到>>> client1
client

 

--结束END--

本文标题: python 协程

本文链接: https://www.lsjlt.com/news/179886.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • python协程
    目录 python协程入门 函数的执行顺序 协程概念 总结 python协程入门 函...
    99+
    2023-01-30
    python 协程
  • python 协程
    进程和线程都会的切换都要消耗时间,保存线程进程当前状态以便下次继续执行。在不怎么需要cpu的程序中,即相对于IO密集型的程序,协程相对于线程进程资源消耗更小,切换更快,更适用于IO密集型。协程也是单线程的,没法利用cpu的多核,想利用cp...
    99+
    2023-01-30
    python 协程
  • python协程与golang协程的区
    进程、线程和协程 进程的定义: 进程,是计算机中已运行程序的实体。程序本身只是指令、数据及其组织形式的描述,进程才是程序的真正运行实例。 线程的定义: 操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。 进程...
    99+
    2023-01-31
    python golang
  • python线程、协程
    线程Threading用于提供线程相关的操作,线程是应用程序中工作的最小单元。更多方法:start            线程准备就绪,等待CPU调度setName      为线程设置名称getName      获取线程名称setDae...
    99+
    2023-01-31
    线程 python 协程
  • Python 协程与 JavaScript 协程的对比
    目录1、前言2、什么是协程?3、混乱的历史3.1Python协程的进化4、JavaScript协程的进化5、Python协程成熟体5.1协程(coroutine)5.2任务(Task...
    99+
    2024-04-02
  • python之协程
      协程,又称微线程,纤程。英文名Coroutine。协程是一种用户态的轻量级线程。  所谓用户态就是说协程是由用户来控制的,CPU不认识协程,协程是跑在线程中的。  协程拥有自己的寄存器上下文栈。协程调试切换时,将寄存器上下文栈保存到其他...
    99+
    2023-01-31
    python
  • Python进程/线程/协程
    第1章 操作系统历史1.1为什么要有操作系统?程序员无法把所有的硬件操作细节全部了解到,管理这些硬件并且加以优化使用时非常繁琐的工作,这个繁琐的工作就是由操作系统来干的,有了它,程序员就从这些繁琐的工作中解脱了出来,只需要考虑自己的应用软件...
    99+
    2023-01-31
    线程 进程 Python
  • Python下的协程
    线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作则是程序员。协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代...
    99+
    2023-01-31
    Python
  • Python协程之Gevent
    协程,又称微线程,即为轻量级的线程。python中实现协程是基于Gevent模块,Gevent模块内部封装了greenlet模块;greenlet模块实现了在单线程中切换状态,Gevent模块在此之上还实现了遇到I/O操作自动切换,使程序运...
    99+
    2023-01-31
    Python Gevent
  • 浅谈Python协程asyncio
    一、协程 官方描述; 协程是子例程的更一般形式。 子例程可以在某一点进入并在另一点退出。 协程则可以在许多不同的点上进入、退出和恢复。 它们可通过 async def 语句来实现。...
    99+
    2024-04-02
  • python协程--asyncio模块(
    在高并发的场景下,python提供了一个多线程的模块threading,但似乎这个模块并不近人如意,原因在于cpython本身的全局解析锁(GIL)问题,在一段时间片内实际上的执行是单线程的。同时还存在着资源争夺的问题。python3.4...
    99+
    2023-01-30
    模块 python 协程
  • python并发之协程
    概念 协程,又称微线程。英文名Coroutine。 子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B,B在执行过程中又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕。所以子程序调用是通过栈实现的,一个线程...
    99+
    2023-01-31
    python
  • Python并发编程之协程
    协程介绍 协程:是单线程下的并发,又称微线程,纤程。协程是一种用户态的轻量级线程,即线程是由用户程序自己控制调度的。 需要强调的是: #1. python的线程属于内核级别的,即由操作系统控制调度(如单线程遇到io或执行时间过长就会被迫...
    99+
    2023-01-30
    Python
  • Python网络编程之协程
    What is the association与子例程一样,协程也是一种程序组件。 相对子例程而言,协程更为一般和灵活,但在实践中使用没有子例程那样广泛。 协程源自Simula和Modula-2语言,但也有其他语言支持。 协程更适合于用来实...
    99+
    2023-01-31
    网络编程 Python
  • Python进程/线程/协程相关
    1、获取进程ID。(getpid)os.getpid()2、获取父进程ID。(getppid)os.getppid()3、获取线程ID。(get_ident)(1)、进程内局部标识。import threading threading.ge...
    99+
    2023-01-31
    线程 进程 Python
  • Python:线程、进程与协程(7)——
         前面转载了一篇分析进程池源码的博文,是一篇分析进程池很全面的文章,点击此处可以阅读。在Python中还有一个线程池的概念,它也有并发处理能力,在一定程度上能提高系统运行效率;不正之处欢迎批评指正。     线程的生命周期可以分为5...
    99+
    2023-01-31
    线程 进程 Python
  • Python:线程、进程与协程(3)——
        Queue模块是提供队列操作的模块,队列是线程间最常用的交换数据的形式。该模块提供了三种队列:Queue.Queue(maxsize):先进先出,maxsize是队列的大小,其值为非正数时为无线循环队列Queue.LifoQueue...
    99+
    2023-01-31
    线程 进程 Python
  • Python:线程、进程与协程(2)—
        上一篇博文介绍了Python中线程、进程与协程的基本概念,通过这几天的学习总结,下面来讲讲Python的threading模块。首先来看看threading模块有哪些方法和类吧。主要有:Thread :线程类,这是用的最多的一个类,...
    99+
    2023-01-31
    线程 进程 Python
  • Python:线程、进程与协程(1)——
            最近的业余时间主要放在了学习Python线程、进程和协程里,第一次用python的多线程和多进程是在两个月前,当时只是简单的看了几篇博文然后就跟着用,没有仔细去研究,第一次用的感觉它们其实挺简单的,最近这段时间通过看书, 看...
    99+
    2023-01-31
    线程 进程 Python
  • Python:线程、进程与协程(6)——
        上篇博文介绍了multiprocessing模块的内存共享(点击此处可以参看),下面讲进程池。有些情况下,所要完成的工作可以上篇博文介绍了multiprocessing模块的内存共享,下面讲进程池。有些情况下,所要完成的工作可以分解...
    99+
    2023-01-31
    线程 进程 Python
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作