python高并发-异步编程

72 阅读2分钟

异步编程

协程存在

  1. 线程阻塞导致的资源开销问题
  2. 并发多开线程资源开销问题

有哪些类型的协程

普通任务类【非阻塞型】

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__方法进而进入死循环