协程技术
基础概念
1.定义:纤程,微线程。是为非抢占式多任务产生子程序的计算机组件。协程允许不同入口点在不同位置暂停或开始,简单来说,协程就是可以暂停执行的函数。
2.协程原理 : 记录一个函数的上下文栈帧,协程调度切换时会将记录的上下文保存,在切换回来时进行调取,恢复原有的执行内容,以便从上一次执行位置继续执行。
3.协程优缺点
优点:
- 协程完成多任务占用计算资源很少
- 由于协程的多任务切换在应用层完成,因此切换开销少
- 协程为单线程程序,无需进行共享资源同步互斥处理
缺点:协程的本质是一个单线程,无法利用计算机多核资源
标准库协程的实现
python3.5以后,使用标准库asyncio和async/await 语法来编写并发代码。asyncio库通过对异步IO行为的支持完成python的协程。虽然官方说asyncio是未来的开发方向,但是由于其生态不够丰富,大量的客户端不支持awaitable需要自己去封装,所以在使用上存在缺陷。更多时候只能使用已有的异步库(asyncio等),功能有限。
第三方协程模
1.greenlet模块
安装 : sudo pip3 install greenlet
函数
greenlet.greenlet(func)
- 功能:创建协程对象
- 参数:协程函数
g.switch()
- 功能:选择要执行的协程函数
1 from greenlet import greenlet 2 3 def test1(): 4 print("执行test1") 5 gr2.switch() 6 print("结束test1") 7 gr2.switch() 8 9 def test2(): 10 print("执行test2") 11 gr1.switch() 12 print("结束test2") 13 14 # 将函数变成协程 15 gr1 = greenlet(test1) 16 gr2 = greenlet(test2) 17 gr1.switch() # 选择执行协程1
2.gevent模块
安装:sudo pip3 install gevent
函数
gevent.spawn(func,argv)
- 功能: 生成协程对象
- 参数:
- func 协程函数
- argv 给协程函数传参(不定参)
返回值: 协程对象
gevent.joinall(list,[timeout])
- 功能: 阻塞等待协程执行完毕
- 参数:
- list 协程对象列表
- timeout 超时时间
gevent.sleep(sec)
- 功能: gevent睡眠阻塞
- 参数:睡眠时间
gevent协程只有在遇到gevent指定的阻塞行为时才会自动在协程之间进行跳转
如gevent.joinall(),gevent.sleep()带来的阻塞
1 import gevent 2 3 4 # 协程函数 5 def foo(a,b): 6 print("Running foo ...",a,b) 7 gevent.sleep(2) 8 print("Foo again") 9 10 def bar(): 11 print("Running bar ...") 12 gevent.sleep(3) 13 print("bar again") 14 15 f = gevent.spawn(foo,1,2) 16 b = gevent.spawn(bar) 17 18 gevent.joinall([f,b]) # 阻塞等待[]中的协成结束
1 """ 2 扩展代码 3 """ 4 5 import gevent 6 from gevent import monkey 7 monkey.patch_all() # 在导入socket前执行 8 from socket import * 9 10 # 处理客户端请求 11 def handle(c): 12 while True: 13 data = c.recv(1024) 14 if not data: 15 break 16 print(data.decode()) 17 c.send(b'OK') 18 c.close() 19 20 # 创建TCP套接字 21 s = socket() 22 s.bind(('0.0.0.0',8888)) 23 s.listen(5) 24 while True: 25 c,addr = s.accept() 26 print("Connect from",addr) 27 # handle(c) # 循环方案 28 gevent.spawn(handle,c) # 协程方案 29 30 s.close()
monkey脚本
作用:在gevent协程中,协程只有遇到gevent指定类型的阻塞才能跳转到其他协程,因此,我们希望将普通的IO阻塞行为转换为可以触发gevent协程跳转的阻塞,以提高执行效率。
转换方法:gevent 提供了一个脚本程序monkey,可以修改底层解释IO阻塞的行为,将很多普通阻塞转换为gevent阻塞。
使用方法:
1.导入monkey
from gevent import monkey
2.运行相应的脚本,例如转换socket中所有阻塞
monkey.patch_socket()
3. 如果将所有可转换的IO阻塞全部转换则运行all
monkey.patch_all()
注意:脚本运行函数需要在对应模块导入前执行