- Greenlet & Gevent的使用
- 前文(Python并发编程-part2)提到使用yield的生成器形成任务切换;缺点是如果单线程有20个任务,则yield方式很麻烦(需要初始化生成器,然后send内容进入生成器)
- 真正的协程模块使用greenlet完成的多任务切换,很轻松
- Gevent 封装了Greenlet,可以不用一定程度减少手动切换的麻烦;
- 对于以下Gevent例子,如果串行实现,大概要4s,Gevent的自动切换使得实际时间只要2s左右;
from greenlet import greenlet def gf(name): print(f"{name}: 吃东西") g2.switch("小张") # 切换到g2任务并且对name初始化,然后suspend等待再次激活 print(f"{name}: 看电影") g2.switch() def bf(name): print(f"{name}: ok") g1.switch() # name初始化过了,不用再传入 print(f"{name}: 下午") if __name__=="__main__": g1 = greenlet(gf) g2 = greenlet(bf) # 切换 g1.switch("小陈") # 切换到g1任务并且对name初始化 ============================== # Gevent改进 import gevent def gf(name): print(f"{name}: 吃东西") # g2.switch("小张") # 切换到g2任务并且对name初始化,然后suspend等待再次激活 gevent.sleep(2) # 注意不是time的sleep,是Gevent自己的sleep(伪IO),才可以自动切换;使用time sleep需要在最前面加上补丁 monkey.patch_all(),monkey来自from genvent import monkey print(f"{name}: 看电影") # g2.switch() def bf(name): print(f"{name}: ok") # g1.switch() # name初始化过了,不用再传入 gevent.sleep(2) print(f"{name}: 下午") if __name__=="__main__": # g1 = greenlet(gf) # g2 = greenlet(bf) g1 = gevent(gf, "小陈") # 更简洁 g2 = gevent(bf, "小张") # 切换 # g1.switch("小陈") # 切换到g1任务并且对name初始化 # 启动任务 g1.join() g2.join() # 或者这两步可以合并为 gevent.joinall([g1,g2])
- async IO 异步IO:python3.4引入,利用事件循环机制处理多并发(图片-尚学堂,注释-我)
import asyncio # 创建协程-旧的方法,通过装饰器 @asyncio.coroutine # before python3.5 def func1(): for i in range(5): print(f"吃瓜-{i}") yield from asyncio.sleep(1) # 创建协程-新的方法,通过关键字 async def func2(): # >= python3.5 for i in range(5): print(f"不吃-{i}") await asyncio.sleep(2) # callback的使用可能是事件加入和IO结束时触发通知 if __name__ == "__main__": g1 = func1() g2 = func2() # 创建事件循环 loop = asyncio.get_event_loop() # 监听事件循环 loop.run_until_complete(asyncio.gather(g1,g2)) # 关闭事件循环 loop.close() =============================== # 测试回调函数callback import asyncio import functools async def compute(x, y): print("compute x + y ...") await asyncio.sleep(1) # 交出cpu控制权 return x+y async def print_result(x, y): # 创建任务以便添加callback task = asyncio.create_task(compute(x,y)) # 添加回调函数 task.add_done_callbback(functools.partial(end, x, y)) for i in range(1000000): if i % 5000 == 0: print(i) asyncio.sleep(0.2) # 要交出cpu控制权,以便回调函数能执行 # 回调函数 def end(x, y, t): # t即task对象 print(f"{x} + {y} = {t.result}") if __name__=="__main__": # 创建事件循环 loop = asyncio.get_event_loop() # 添加事件、任务 loop.run_until_complete(print_result(2, 3)) # 关闭事件循环 loop.close()
- 总结
- 串行、并行、并发(图片-尚学堂)
- 进程是系统分配资源的最小单位,线程是程序执行的最小单位;线程上下文切换快得多;
- 串行、并行、并发(图片-尚学堂)
python并发编程-part3
最新推荐文章于 2024-12-15 17:38:28 发布