基础概念
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]) # 阻塞等待[]中的协成结束
gevent 协程示例
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()
基于协程的tcp并发
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()
注意:脚本运行函数需要在对应模块导入前执行