这是我参与8月更文挑战的第8天,活动详情查看:8月更文挑战
一、协程的实现
1、事件循环
理解成为一个死循环,去检测并执行某些代码。
伪代码
任务列表 = [任务1, 任务2, 任务3, ...]
while True:
可执行的任务列表, 已完成的任务列表 = 去任务列表中检查所有的任务, 将"可执行"和"已执行"的任务返回
for 就绪任务 in 已准备就绪的任务列表:
执行已就绪任务
for 已完成的任务 in 已完成的任务列表:
在任务列表中移除已完成的任务
如果任务列表中的任务都已完成,则终止循环
import asyncio
# 去生成或者获取一个事件循环
loop = asyncio.get_event_loop()
# 将任务放到任务列表
loop.run_until_complete("任务")
2、快速上手
协程函数,定义函数时候 async def 函数名。
协程对象,执行协程函数()得到的协程对象。
async def func():
pass
result = func()
注意:执行协程函数创建协程对象,很熟内部代码不会执行。
如果想要运行协程函数内部代码,必须要将协程对象交给时间循环来处理。
import asyncio
async def func():
print("我是不是被执行了!")
result = func()
# loop = asyncio.get_event_loop()
# loop.run_until_complete(result)
asyncio.run(result) # python3.7
3、 await
await + 可等待的对象(协程对象、Future、Task对象->IO等待)
示例1:
import asyncio
async def func():
print('start')
response = await asyncio.sleep(2)
print('result:{}'.format(response))
asyncio.run(func())
运行结果如下:
示例2:
import asyncio
async def other():
print('start')
await asyncio.sleep(2)
print('end')
return 'result'
async def func():
print('执行协程函数内部代码')
# 遇到IO操作挂起当前协程(任务), 等IO操作完成之后再继续往下执行,当前线程挂起时,事件循环可以去执行其他协程(任务)
response = await other()
print('IO 请求结束,结果为:{}'.format(response))
asyncio.run(func())
运行结果如下:
示例3:
import asyncio
async def other():
print('start')
await asyncio.sleep(2)
print('end')
return 'result'
async def func():
print('执行协程函数内部代码')
# 遇到IO操作挂起当前协程(任务), 等IO操作完成之后再继续往下执行,当前线程挂起时,事件循环可以去执行其他协程(任务)
response1 = await other()
print('IO 请求结束,结果为:{}'.format(response1))
response2 = await other()
print('IO 请求结束,结果为:{}'.format(response2))
asyncio.run(func())
运行结果如下:
4、 Task对象
Tasks用于并发调度协程,通过asyncio.create_task(协程对象)的方式创建Task对象,这样可以让协程加入事件循环中等待被调度执行。除了使用asyncio.create_task()函数意外,可以使用低层级的loop.create_task()或ensure_future()函数。不建议手动实例化Task对象。
注意:asyncio.create_task()函数在Python3.7中被加入。在Python3.7之前,可以改用低层次的asyncio.ensure_future()函数。
示例1:
import asyncio
async def func():
print(1)
await asyncio.sleep(2)
print(2)
return "返回值"
async def main():
print("main开始")
# 创建Task对象,将当前执行func函数任务添加到事件循环
task1 = asyncio.create_task(func())
task2 = asyncio.create_task(func())
print("main结束")
# 当执行某协程遇到IO操作时,会自动化切换执行其他任务
# 此处的await是等待相对应的协程全都执行完毕并获取结果
result1 = await task1
result2 = await task2
print(result1, result2)
asyncio.run(main())
运行结果如下:
示例2:
import asyncio
async def func():
print(1)
await asyncio.sleep(2)
print(2)
return "返回值"
async def main():
print("main开始")
task_list = [
asyncio.create_task(func()),
asyncio.create_task(func())
]
print("main结束")
down, pending = await asyncio.wait(task_list, timeout=None)
print(down)
asyncio.run(main())
运行结果如下:
示例3:
import asyncio
async def func():
print(1)
await asyncio.sleep(2)
print(2)
return "返回值"
task_list = [
func(),
func(),
]
down, pending = asyncio.run(asyncio.wait(task_list))
print(down)
运行结果如下:
5、asyncio.Future对象
Task继承Future,Task对象内部await结果的处理基于Future对象来的。
示例1:
import asyncio
async def main():
# 获取当前事件循环
loop = asyncio.get_running_loop()
# 创建一个任务(Future对象), 这个任务什么都不干。
fut = loop.create_future()
# 等待任务最终结果(Future对象),没有结果则会一直等下去。
await fut
asyncio.run(main())
示例2:
import asyncio
async def set_after(fut):
await asyncio.sleep(2)
fut.set_result("666")
async def main():
# 获取当前事件循环
loop = asyncio.get_running_loop()
# 创建一个任务(Future对象), 这个任务什么都不干。
fut = loop.create_future()
# 创建一个任务(Task对象),绑定set_after函数,函数内部在2s之后,会给fut赋值。
# 即手动设置future任务的最终结果,fut就可以结束。
await loop.create_task(set_after(fut))
# 等待任务最终结果(Future对象),没有结果则会一直等下去。
data = await fut
print(data)
asyncio.run(main())
运行结果如下:
6、concurrent.futures.Future对象
使用线程池,进程池实现异步操作时用到的对象。
import time
from concurrent.futures.thread import ThreadPoolExecutor
from concurrent.futures.process import ProcessPoolExecutor
def func(value):
time.sleep(1)
print(value)
# 创建线程池
pool = ThreadPoolExecutor(max_workers=5)
# 创建进程池
pool1 = ProcessPoolExecutor(max_workers=5)
for i in range(10):
fut = pool.submit(func, i)
print(fut)
运行结果如下:
以后写代码可能会存在交叉时间。例如:crm项目80%都是基于协程异步编程+MySQL(不支持)【线程、进程做异步编程】。
import asyncio
import concurrent
import time
from concurrent.futures.thread import ThreadPoolExecutor
from concurrent.futures.process import ProcessPoolExecutor
def func1():
# 某个耗时操作
time.sleep(2)
return "SSS"
async def main():
loop = asyncio.get_running_loop()
'''
1、Run in the default loop's executor(默认ThreadPoolExecutor)
第一步:内部回先调用ThreadPoolExecutor的submit方法去线程池中申请一个线程去执行func1函数,并返回一个concurrent.futures.Future对象
第二步:调用asyncio.wrap_future将concurrent.futures.Future对象包装为asyncio.Future对象。
因为concurrent.futures.Future不支持await语法,所以需要包装为asyncio.Future对象才能使用
'''
fut = loop.run_in_executor(None, func1)
result = await fut
print("default thread pool", result)
# # 2、Run in a custom thread pool
# with concurrent.futures.ThreadPoolExecutor() as pool:
# result = await loop.run_in_executor(pool, func1)
# print("custom thread pool:", result)
#
# # 3、Run in a custom process pool
# with concurrent.futures.ProcessPoolExecutor() as pool:
# result = await loop.run_in_executor(pool, func1)
# print("custom process pool:", result)
asyncio.run(main())
运行结果如下:
案例:asyncio +不支持异步的模块
import asyncio
import concurrent
import time
from concurrent.futures.thread import ThreadPoolExecutor
from concurrent.futures.process import ProcessPoolExecutor
import requests
async def down_img(url):
# 发送网络请求,下载图片(遇到网络下载图片的IO请求,自动化切换到其他任务)
print("开始下载:", url)
loop = asyncio.get_event_loop()
# requests 模块默认不支持异步操作,所以就使用线程池来配合实现
future = loop.run_in_executor(None, requests.get, url)
response = await future
file_name = url.split('/')[-1]
with open(file_name, 'wb') as f:
f.write(response.content)
print("下载完成")
if __name__ == "__main__":
url_list = [
'https://img.tupianzj.com/uploads/allimg/202003/9999/800802cd3b.jpg',
'https://img.tupianzj.com/uploads/allimg/202003/9999/0dfc390267.jpg',
'https://img.tupianzj.com/uploads/allimg/160206/9-1602061H626.jpg',
'https://img.tupianzj.com/uploads/allimg/202003/9999/064a2dff13.jpg',
'https://img.tupianzj.com/uploads/allimg/160125/9-160125101553.jpg'
]
task_list = [down_img(url) for url in url_list]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(task_list))
运行结果如下:
7、异步迭代器
什么是异步迭代器
实现了__aiter__()和__anext__()方法的对象。anext()必须返回一个awaitable对象。async_for会处理异步迭代器的__anext__()方法所返回的可等待对象,知道其引发一个StopAsyncIteration异常。由PEP 492引入。
什么是异步可迭代对象?
可在async_for语句中被使用的对象。必须通过他的__aiter__()方法返回一个asynchronous iterator。由PEP 492引入。
import asyncio
class Reader(object):
'''自定义异步迭代器(同时也会异步可迭代对象)'''
def __init__(self):
self.count = 0
async def read_line(self):
# await asyncio.sleep(1)
self.count += 1
if self.count == 100:
return None
return self.count
def __aiter__(self):
return self
async def __anext__(self):
val = await self.read_line()
if val == None:
raise StopAsyncIteration
return val
async def func():
obj = Reader()
async for item in obj:
print(item)
asyncio.run(func())
运行结果如下:
8、异步上下文管理器
此种对象通过定义__aiter__()和__anext__()方法对async_with语句中的环境进行控制。
import asyncio
class AsyncContextManger:
def __init__(self, conn):
self.conn = conn
async def do_something(self):
# 异步操作数据库
return 666
async def __aenter__(self):
# 异步连接数据库
# self.conn = asyncio.sleep(1)
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
# 异步关闭数据库
await asyncio.sleep(1)
async def func():
async with AsyncContextManger("conn") as f:
result = await f.do_something()
print(result)
asyncio.run(func())
运行结果如下: