协程,又称微线程,纤程。英文名Coroutine。
https://www.cnblogs.com/coder-qi/p/10163416.html
协程不是计算机提供的,是人为创造的上下文切换技术,也可以被称为微线程。简而言之 其实就是在一个线程中实现代码块相互切换执行。
可以采用以下几种基于协程的方式:
# greenlet是第三方模块需先引入
pip3 install greenlet
# -*- coding: utf-8 -*-
from greenlet import greenlet
def func1():
print(1) # 第二步:输出1
gr2.switch() # 第三步:切换到 func2 函数
print(2) # 第六步:输出2
gr2.switch() # 第七步:切换到func2 函数(如果不切换的话句柄会继续往下执行,也就不会进入func2 输出4)
def func2():
print(3) # 第四步:输出3
gr1.switch() # 第五步:切换到func1 函数
print(4) # 第八步:输出4,func2函数 执行完毕句柄继续往下执行
def func3():
print(5) # 第十步:输出5
gr1 = greenlet(func1) # 此处只是生成 greenlet 包装的 func1 对象,代码并不会实际运行
gr2 = greenlet(func2) # 此处生成 greenlet 包装的 func2 对象
gr1.switch() # 第一步:此处是正式执行 func1() 对象
func3() # 第九步:实例化 func3
运行结果:
1
3
2
4
5
不推荐,实际应用场景比较少。
实例1:
def func1():
yield 1
yield from func2() # 这里其实相当于for item in func2(): yield item
yield 2
def func2():
yield 3
yield 4
for item in func1():
print(item)
运行结果
1
3
4
2
实例2:
参考:https://www.cnblogs.com/coder-qi/p/10163416.html
import time
def test1():
index=0
while True:
#print("--test1--")
yield f"test1 {index}"
time.sleep(0.5)
index += 1
def test2():
index=0
while True:
#print("--test2--")
yield f"test2 {index}"
time.sleep(0.5)
index += 1
if __name__ == "__main__":
t1 = test1()
t2 = test2()
while True:
print(next(t1))
print(next(t2))
运行结果:
test1 0
test2 0
test1 1
test2 1
test1 2
test2 2
test1 3
test2 3
参考:https://www.cnblogs.com/micheryu/p/15779377.html
在 python3.4 及之后的版本才可使用,这个框架使用事件循环来编排回调和异步任务。事件循环位于事件循环策略的上下文中。
下图是协程,事件循环和策略之间的相互作用
注意:asyncio 牛逼在于遇到 IO 阻塞自动切换!
下面我们使用 @asyncio.coroutine 装饰器(py3.10+会移除) 定义了两个协程函数。(基于生成器的协程)
import asyncio
@asyncio.coroutine
def func1():
print(1)
# 此处用asyncio.sleep(2)来模拟IO耗时(asyncio.sleep也是一个协程对象,不能用time.sleep()),asyncio定义的协程函数遇到IO操作时
会自动切换到事件循环中的其他任务
yield from asyncio.sleep(2)
print(2)
# 暂未使用
@asyncio.coroutine
def func2():
print(3)
yield from asyncio.sleep(2)
print(4)
print(asyncio.iscoroutine(func1()))
loop = asyncio.get_event_loop()
loop.run_until_complete(func1())
运行结果:
/*/demo.py:4: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
def func1():
/*/demo.py:11: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
def func2():
True
/*/demo.py:18: DeprecationWarning: There is no current event loop
loop = asyncio.get_event_loop()
1
2
PS:如果 py 版本高于3.8依然可以使用 asyncio.coroutine 装饰器但是会有告警建议你使用 async & await 关键字来定义协程函数,不会影响使用!
协程函数并不能像普通函数一样直接实例化运行,调用协程函数协程并不会开始运行,只是返回一个协程对象。可以通过 asyncio.iscoroutine 来验证是否是协程对象。
print(asyncio.iscoroutine(func1())) # True
协程对象必须在事件循环中运行,我们可以通过 asyncio.get_event_loop 方法来获取当前正在运行的循环实例。如 loop 对象,然后把协程对象交给 loop.run_until_complete,协程对象随后会在 loop 里得到运行。
run_until_complete 是一个阻塞(blocking)调用,直到协程运行结束,它才返回;所以他必须接受的是一个可等待对象(协程,任务 和future对象)。
协程对象:协程函数实例化后就是协程对象
future 对象:asyncio.futures.Future 对象用来链接 底层回调式代码和高层异步/等待式代码,可以简单理解为 future 对象是可以使程序 hang在某个地方等待有结果了之后再继续执行。
import asyncio
async def main():
# 获取当前事件循环
loop = asyncio.get_running_loop()
# 单单只是创建一个future对象
fut = loop.create_future()
# future对象因为什么都没做也就没返回值,所以 await 会一直等待下去程序就会 hang 住
await fut
asyncio.run(main())
# 这句永远都不会执行
print(1)
async def func(fut):
fut.set_result("finish")
async def main():
# 获取当前事件循环
loop = asyncio.get_running_loop()
# 单单只是创建一个future对象
fut = loop.create_future()
# 创建一个task对象,绑定了func函数并且把我们创建的fut对象传递给了协程对象func;func协程函数内部又对fut对象设置了result
await loop.create_task(func(fut))
# 由于设置了fut对象的结果,下面的await就能拿到结果 所以程序就可以继续往下执行了
print(await fut)
asyncio.run(main())
print(1)
运行结果:
finish
1
任务:
参考:https://www.cnblogs.com/micheryu/p/15779377.html
py3.5 及之后版本
本质上和3.4的 asyncio 一致,但更强大。
3.5之后 yield from 不可以在 async 定义的函数内使用,需使用 await。
实例1:
参考:https://blog.csdn.net/u011089760/article/details/90542894
import asyncio
import time
async def job(t): # async 形式的功能
print('Start job ', t)
# 等待 "t" 秒, 期间切换其他任务。
# 此处用 asyncio.sleep(t) 来模拟 IO 耗时(asyncio.sleep 也是一个协程对象,不能用 time.sleep())
# asyncio 定义的协程函数遇到 IO 操作时会自动切换到事件循环中的其他任务
await asyncio.sleep(t)
print('Job ', t, ' takes ', t, ' s')
async def main(loop): # async 形式的功能
tasks = [
loop.create_task(job(t)) for t in range(1, 4)
] # 创建任务, 但是不执行
await asyncio.wait(tasks) # 执行并等待所有任务完成
t1 = time.time()
loop = asyncio.get_event_loop() # 建立 loop
loop.run_until_complete(main(loop)) # 执行 loop,并且等待所有任务结束
loop.close() # 关闭 loop
print("Async total time : ", time.time() - t1)
运行结果:
Start job 1
Start job 2
Start job 3
Job 1 takes 1 s
Job 2 takes 2 s
Job 3 takes 3 s
Async total time : 3.0052335262298584
实例2:
import asyncio
async def func1():
print(1)
await asyncio.sleep(2) # 遇到IO自动切换任务;等待2秒, 期间切换其他任务
print(2)
async def func2():
print(3)
await asyncio.sleep(2) # 此处又遇到IO阻塞后,又会自动切换到tasks中其他的任务
print(4)
tasks = [
asyncio.ensure_future(func1()), # 把协程对象包转成一个 future 对象
asyncio.ensure_future(func2())
]
loop = asyncio.get_event_loop()
# 执行单个协程函数
loop.run_until_complete(func1()) # 由于func1中使用了await关键字所以此处等同于asyncio.wait
""" 输出结果为:
1
等待2s
2
"""
# 执行多个协程函数
loop.run_until_complete(asyncio.wait(tasks))
""" 输出结果为:
1
3
等待2s
2
4
"""
注:python3.7 之后可以不需要自己获取 loop 对象,可以直接调用 asyncio.run() 方法内部已经帮我们获取了 loop 对象和调用loop.run_until_complete(),但是不支持同时运行多个协程对象。
asyncio.run(func1())
async & await 关键字简化代码的同时并且兼容基于生成器的老式协程。
@asyncio.coroutine
def old_style_coroutine():
yield from asyncio.sleep(1)
async def main():
await old_style_coroutine()
一个协程函数中可以使用多次 await 关键字。
import asyncio
async def func():
print("start")
await asyncio.sleep(5)
print("end")
return "finish"
async def main():
print("执行main方法")
resp1 = await func()
print(f"第一次返回值:{resp1}")
resp2 = await func()
print(f"第二次返回值:{resp2}")
asyncio.run(main())
运行结果:
执行main方法
start
end
第一次返回值:finish
start
end
第二次返回值:finish
同样我们也可以在一个协程函数中获取多个其他协程对象的返回值:
import asyncio
async def func():
print(1)
await asyncio.sleep(2)
print(2)
return f"func was done"
async def main():
print("main开始")
# 创建协程,将协程封装到Task对象中并添加到事件循环的任务列表中,等待事件循环去执行(默认是就绪状态)。
# 在调用
task_list = [
asyncio.create_task(func(), name="n1"),
asyncio.create_task(func(), name="n2")
]
print("main结束")
# 当执行某协程遇到IO操作时,会自动化切换执行其他任务。
# 此处的await是等待所有协程执行完毕,并返回两个集合 done、pending。done存放已完成的task对象,pending存放未完成的task对象
# 如果设置了timeout值,则意味着此处最多等待的秒,完成的协程返回值写入到done中,未完成则写到pending中。
done, pending = await asyncio.wait(task_list, timeout=None)
print("done", done)
print("pending", pending)
for coroi in done:
print(coroi.result())
#print(list(done)[0].result())
asyncio.run(main())
运行结果:
main开始
main结束
1
1
2
2
done {<Task finished name='n1' coro=<func() done, defined at /*/demo.py:3> result='func was done'>, <Task finished name='n2' coro=<func() done, defined at /*/demo.py:3> result='func was done'>}
pending set()
func was done
func was done
普通函数调用协程函数:
async def func3():
print('i`m func3')
await asyncio.sleep(2)
print('func3 finished')
def func4():
print('i`m func4')
asyncio.run(func3())
print('func4 finished')
func4()
"""输出结果:
i`m func4
i`m func3
等待2s
func3 finished
func4 finished
"""
协程函数调用普通函数
def func5():
print('i`m func5')
time.sleep(2)
print('func5 finished')
async def func6():
print('i`m func6')
func5()
print('func6 finished')
asyncio.run(func6())
print('all finish')
"""
i`m func6
i`m func5
等待2s
func5 finished
func6 finished
all finish
"""
参考:(没有分析完)
https://blog.csdn.net/weixin_44689630/category_8872679.html
https://blog.csdn.net/weixin_44689630/article/details/122509483