异步编程
协程存在
- 线程阻塞导致的资源开销问题
- 并发多开线程资源开销问题
有哪些类型的协程
普通任务类【非阻塞型】
import asyncio
import os
async def task1(name,count):
await asyncio.sleep(count)
print(name,os.getpid())
async def main():
await asyncio.gather(
task1('task1',1),
task1('task2',2)
)
if __name__ == '__main__':
asyncio.run(main())
普通任务【阻塞型】
import asyncio
import concurrent.futures
import os
import time
def cal_task():
time.sleep(3)
print('block',os.getpid())
async def task1():
await asyncio.sleep(1)
print('->',os.getpid())
async def main():
loop = asyncio.get_event_loop()
await asyncio.gather(
loop.run_in_executor(None,cal_task),
task1()
)
if __name__ == '__main__':
asyncio.run(main())
concurrent将阻塞任务转为线程池处理。查看run_in_executor的源码可以知道,它默认会启动池处理。因此要加速的话,那么executor=concurrent. futures.ThreadPoolExecutor(max_workers=5),然后将None修改为executor即可
高密集计算【阻塞型】
import asyncio
import concurrent.futures
import os
import time
from numba import jit
@jit(nopython=True)
def cal_task():
x = 0
for i in range(5000):
x += i
print('block', os.getpid())
async def task1():
await asyncio.sleep(1)
print('->', os.getpid())
async def main():
loop = asyncio.get_event_loop()
executor = concurrent.futures.ProcessPoolExecutor(max_workers=2)
await asyncio.gather(
loop.run_in_executor(executor, cal_task),
task1()
)
if __name__ == '__main__':
asyncio.run(main())
numba,可以实现高速计算,但是限制很多不能try,不兼容python的list,很多用到list的地方要改成numpy的数组
文件任务【阻塞型】
import asyncio
async def main():
loop = asyncio.get_running_loop()
f = await loop.run_in_executor(None, open, "data.txt", 'w')
await loop.run_in_executor(None, f.write, "aio file")
await loop.run_in_executor(None, f.close)
if __name__ == '__main__':
asyncio.run(main())
文件封装
import asyncio
from typing import Any
class AsyncFunWrapper:
def __init__(self, blocked_fun) -> None:
super().__init__()
self._blocked_fun = blocked_fun
def __call__(self, *args):
return asyncio.get_running_loop().run_in_executor(None, self._blocked_fun, *args)
class AIOWrapper:
def __init__(self, blocked_file_io) -> None:
super().__init__()
self._blocked_file_io = blocked_file_io
def __getattribute__(self, name: str) -> Any:
blocked_attr = super().__getattribute__('_blocked_file_io')
#5.通过继承父类的属性,获得_blocked_file_io属性
#6.然后下面根据这个属性_blocked_file_io,去调用这个属性里面的属性
#getattr(blocked_attr, name)返回的是name的值,实际是将name string->属性write
#相当于f.write
return AsyncFunWrapper(getattr(blocked_attr, name))
async def open_async(*args):
return AIOWrapper(
await asyncio.get_running_loop().run_in_executor(None, open, *args)
)
#3.完成AIOWrapper的初始化__init__,并启动一个线程池来实例化open的文件对象
async def main():
f = await open_async("data.txt", "w")#2.调用异步方法,同时协程切换
await f.write("aio file")
#4.在AIOWrapper找write方法->属性,找不到默认使用__getattribute__处理属性
#7.将属性变成执行方法,并传递参数aio file
await f.close()
if __name__ == '__main__':
asyncio.run(main())#1.异步启动调用main方法
知识补充
class G:
def __init__(self):
print(1)
def __call__(self, *args, **kwargs):
print(2)
def p(self):
print('k')
if __name__ == '__main__':
gg = G()
gg.p()
print(gg())
在初始实例的时候使用__init__,在调用实例的时候使用__call__;在实例调用方法的时候不会使用的__call__
为何使用super().getattribute
class AIOWrapper:
def __init__(self, blocked_file_io) -> None:
super().__init__()
self._blocked_file_io = blocked_file_io
def __getattribute__(self, name: str) -> Any:
blocked_attr = super().__getattribute__('_blocked_file_io')
return AsyncFunWrapper(getattr(blocked_attr, name))
如果使用self._blocked_file_io ,会导致他默认调用getattr方法,同时getattr又默认调用__getattribute__方法进而进入死循环