python并发、异步—协程
单个线程下实现并发,伪并发,适合io密集型的项目
本文只讲3.5之后的async、await关键字
基础
asyncio该库是python3.4之后出的,支持并发、异步、协程
一些关键字说明:
async
:声明协程的关键字,在定义函数关键字之前
await
:用于挂起阻塞的异步调用接口,后边只能跟__wait__
的对象或者是携程
get_running_loop
:程序开启一个无限循环,把一些函数注册到事件循环上,当满足事件发生的时候,调用相应的协程函数
coroutine
:协程对象,指一个使用async关键字定义的函数,它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环,由事件循环调用
task
:(任务)一个协程对象就是一个原生可以挂起的函数,任务则是对协程进一步封装,其中包含了任务的各种状态
future
:代表将来执行或没有执行的任务的结果。它和task上没有本质上的区别
run_in_executor
把普通函数转化成asyncio的future对象
- 定义协程
协程函数
import asyncio, time
async def func(n):
time.sleep(n)
print("lynn", n)
run_func = func(5) # 协程对象
loop = asyncio.get_event_loop() # 开启一个循环
loop.run_until_complete(run_func) # 把写成注册到循环里
这样是阻塞的,同步的
协程对象内部代码不执行
被async声明的函数就是一个协程,不能像一般函数那样调用会报:
sys:1: RuntimeWarning: coroutine 'func' was never awaited
- 调用协程
1 python3.7之前
import asyncio
async def func():
print("async")
return "async"
if __name__ == '__main__':
loop = asyncio.get_event_loop() # 获取事件循环
# task = loop.create_task(func())
# 或者
task = asyncio.ensure_future(func()) # 将任务放到任务列表
res = loop.run_until_complete(task)
print("res:", res)
2 python3.7之后
import asyncio
async def func():
print("async")
return "async"
if __name__ == '__main__':
res = asyncio.run(func())
print("res:", res)
- 创建task(任务)
协程不能直接调用,在注册事件循环的时候,其实是run_until_complete方法将协程包装成为了一个任务(task)对象。task对象是Future类的子类,保存了协程运行后的状态,用于未来获取协程的结果
import asyncio, time
async def func(n):
time.sleep(n)
print("lynn", n)
run_func = func(5)
loop = asyncio.get_event_loop()
task = loop.create_task(run_func)
print(task) # 该状态是 <Task pending coro=<func() running at /Users/tianzhh/Desktop/es_learn/as.py:4>>
loop.run_until_complete(task)
print(task) # 该状态是 <Task finished coro=<func() done, defined at /Users/tianzhh/Desktop/es_learn/as.py:4> result=None>
print("ok")
是阻塞的,同步
- 绑定回调
task运行完成拿不到返回值,通过回调拿到返回值,然后操作
import asyncio, time
async def func(n):
time.sleep(n)
print("lynn", n)
return "lynn-{}".format(n)
def call_back_func(future):
print("result", future.result()) # 拿到协程的返回值
run_func = func(5)
loop = asyncio.get_event_loop()
task = loop.create_task(run_func)
task.add_done_callback(call_back_func) # 加入回调函数
loop.run_until_complete(task)
print("ok")
详细用法
await
后边跟可等待的对象(协程对象、Future对象、Task对象)
import asyncio
async def other():
await asyncio.sleep(2)
async def func():
print("开始")
response = await other()
print("结束", response)
print("开始")
response1 = await other()
print("结束", response1)
if __name__ == '__main__':
asyncio.run(func())
注意:
遇到await
,线程会被阻塞,只有运行完毕后,线程才会继续往下运行。
但是不是同步,是切到别的任务中
task对象
用于并发调度多个协程
import asyncio
async def other():
await asyncio.sleep(2)
async def func():
other_task = asyncio.create_task(other()) # 创建task,加到事件循环中
response = await other_task # 等待运行完,才继续往下进行
print("response:", response)
if __name__ == '__main__':
asyncio.run(func())
如果是多个task,一般用这种
import asyncio
async def other():
print("开始")
await asyncio.sleep(2)
print("结束")
return 100
async def func():
task_list = [
asyncio.create_task(other(), name="task1"),
asyncio.create_task(other(), name="task1"),
]
done, pending = await asyncio.wait(task_list, timeout=10)
print("response:", done, pending)
if __name__ == '__main__':
asyncio.run(func())
注意:
asyncio.wait()
中参数是列表或者元组,切其中的元素可以是:协程对象、task对象、Future对象
asyncio.create_task()
该方法,会在创建task对象的同时,会把task对象加入到事件循环中,所以使用该方法,要先创建时间循环
异步编程
重点:一般用这个
import time, asyncio
def f1(n, m):
print(n, m)
time.sleep(n)
print("普通函数{}".format(n, m))
return n, m
async def main():
loop = asyncio.get_event_loop()
f = loop.run_in_executor(None, f1, 2, 3)
g = loop.run_in_executor(None, f1, 1, 3)
f_result = await f
g_result = await g
print("result:", f_result)
print("result:", g_result)
print("完")
asyncio.run(main())
print("主")
注意:
run_in_executor
中只能是普通函数,不能是协程函数,会把普通函数转化为asyncio的future对象,然后await后异步调用不等待,但是会等所有的调用运行完,才继续往下运行