协程技术

基础概念

1.定义:纤程,微线程。是为非抢占式多任务产生子程序的计算机组件。协程允许不同入口点在不同位置暂停或开始,简单来说,协程就是可以暂停执行的函数。

2.协程原理 : 记录一个函数的上下文栈帧,协程调度切换时会将记录的上下文保存,在切换回来时进行调取,恢复原有的执行内容,以便从上一次执行位置继续执行。

3.协程优缺点

优点:

  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()

注意:脚本运行函数需要在对应模块导入前执行

原文地址:https://www.cnblogs.com/maplethefox/p/11056047.html