- 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()
- 总结
- 串行、并行、并发(图片-尚学堂)
- 进程是系统分配资源的最小单位,线程是程序执行的最小单位;线程上下文切换快得多;