协程 (corountine):又称微线程。
asyncio,在单线程利用CPU和IO同时执行的原理,实现函数异步执行。
实现协程就是要多个任务的循环,await是挂起命令。每到一个地方await一次,然后await返回。知道最终全部返回,程序结束。
协程代码之间不断切换,形成的闭环范围作为一个事件循环.
#获取事件循环
loop = asyncio.get_event_loop()
协程对象,指一个使用async关键字定义的函数。它的调用不会立即执行函数,需要注册到事件循环,由事件循环调用。
本质:被 async 关键定义的协程,实际上是一个Coroutine对象,而Coroutine又继承Awaitable
from collections.abc import Coroutine, Awaitable
# 定义协程函数
async def work(self):
while self.que.qsize() > 0:
try:
url = self.que.get(timeout=1)
await asyncio.sleep(1)
print(f"{os.getpid()}正在请求url:{url}")
return os.getpid()
except Exception as e:
import traceback
print("---------------err", traceback.format_exc())
break
具体执行的任务
# 方法1:使用ensure_future方法。future代表一个对象,未执行的任务。
task1 = asyncio.ensure_future(func1(1))
task2 = asyncio.ensure_future(func1(2))
# 方法3:使用Python 3.7提供的asyncio.create_task方法
task1 = asyncio.create_task(func1(1))
task2 = asyncio.create_task(func1(2))
创建Future对象的时候,状态为pending,事件循环调用执行的时候就是running,调用完毕就是done,如果需要取消Future对象的调度执行,可调用Future对象的cancel()函数。
result():立即返回Future对象运行结果或者抛出执行时的异常,没有timeout参数,如果Future没有完成,不会阻塞等待结果,而是直接抛出InvalidStateError异常。最好的方式是通过await获取运行结果,await会自动等待Future完成返回结果,也不会阻塞事件循环,因为在asyncio中,await被用来将控制权返回给事件循环。
done():非阻塞的返回Future对象是否成功取消或者运行结束或被设置异常,而不是查看future是否已经执行完成。
cancelled():判断Future对象是否被取消。
add_done_callback():传入一个可回调对象,当Future对象done时被调用。
exception():获取Future对象中的异常信息,只有当Future对象done时才会返回。
get_loop():获取当前Future对象绑定的事件循环。
#获取时间循环
loop = asyncio.get_event_loop()
#创建任务任务asyncio.ensure_future
task = [asyncio.ensure_future(work1(i)) for i in range(5)]
# 将任务注册到时间循环里
loop.run_until_complete(asyncio.wait(task))
loop.close()
这里使用asyncio.gather收集任务列表,无需创建函数
import asyncio
async def func1(i):
print(f"协程函数{i} 马上执行")
await asyncio.sleep(2)
print(f"协程函数{i} 执行完毕!")
async def main():
tasks = []
for i in range(1, 5):
#这里未由协程函数创建协程任务
tasks.append(func1(i))
# gather自动将函数列表封装成了协程任务
await asyncio.gather(*tasks)
if __name__ == '__main__':
asyncio.run(main())
注意点: 协程的实现函数必须要通过await asyncio.sleep(time)来实现。 time.sleep是没效果的
asyncio.wait 会返回两个值:done 和 pending,done 为已完成的协程任务列表,pending 为超时未完成的协程任务类别,需通过task.result()方法可以获取每个协程任务返回的结果;
asyncio.gather 返回的是所有已完成协程任务的 result,不需要再进行调用或其他操作,就可以得到全部结果。
import asyncio
async def func1(i):
print(f"协程函数{i}马上执行。")
await asyncio.sleep(2)
return i
async def main():
tasks = []
for i in range(1, 5):
tasks.append(asyncio.create_task(func1(i)))
# 获取任务结果
done, pending = await asyncio.wait(tasks)
for task in done:
print(f"执行结果: {task.result()}")
if __name__ == '__main__':
asyncio.run(main())
demo2: 使用loop_run 执行,task.result 返回参数
def run(self):
task = []
# 开启多协程
loop = asyncio.get_event_loop()
for i in range(1000):
# 调用工作函数
task.append(asyncio.ensure_future(self.work()))
loop.run_until_complete(asyncio.wait(task))
#loop.run_until_complete(asynciogather(*tasks)) #这种有序返回也可以
res = [t.result() for t in task ]
loop.close()
print(res)
# gather自动将函数列表封装成了协程任务
results = await asyncio.gather(*tasks)
for res in results:
print("-------------res",res)
import asyncio
import os
import time
from multiprocessing import Process, Queue
def time_count(func):
def wrapper(*args, **kwargs):
start_time = time.time()
func(*args, **kwargs)
end_time = time.time()
print("总耗时:", end_time - start_time)
return wrapper
class Myprocess(Process):
def __init__(self, que):
super().__init__()
self.que = que
# 重写run方法
def run(self):
task = []
# 开启时间循环
loop = asyncio.get_event_loop()
for i in range(1000):
# 调用工作函数
task.append(asyncio.ensure_future(self.work()))
loop.run_until_complete(asyncio.wait(task))
# loop.run_until_complete(asyncio.gather(*task)) 两种方法都可以
res = [t.result() for t in task]
loop.close()
print(res)
# 定义工作函数
async def work(self):
while self.que.qsize() > 0:
try:
url = self.que.get(timeout=1)
await asyncio.sleep(1)
print(f"{os.getpid()}正在请求url:{url}")
return os.getpid()
except Exception as e:
break
@time_count
def main():
q = Queue()
for i in range(10000):
q.put(f"https://www.baidu.com--{i}")
process_list = []
for i in range(4):
p = Myprocess(q)
process_list.append(p)
p.start()
for p in process_list:
p.join()
print("任务结束")
if __name__ == '__main__':
main()
# 使用asyncio改写, 加入函数返回值
# 总耗时: 5.137028455734253