asnycio 异步请求+异步回调

关于 asyncio 模块这里不做详细的介绍,如果你还不认识他,请前往 链接 自己查询吧。

我这里就假设大家都已经了解了 asyncio 这个异步io 模块的基本用法。

背景:

​ 最近我刚刚了解到这个新的异步io 的模块,之前我只是知道 gevent。在学习这个模块的时候我发现在创建任务时可以手动的给这个任务绑定一个回调函数,而不用在自己去实现这个功能。可以说是非常的方便了。

简单的创建任务的代码如下:

# -*- coding:utf-8 -*-
import asyncio

async def async_func():
    pass


def demo_clall_back_func(task):
    pass


task = asyncio.ensure_future(async_func())
task.add_done_callback(demo_clall_back_func)

创建任务以及绑定任务回调的 demo 就是这样的。(async 函数内部请自行脑补一下。)

此时我就想到了一个问题: 如果现在我的 async_func 函数内是异步获取网页,demo_clall_back_func 函数的功能是把数据做持久化(例如:写入文件中)。

  1. 那么是谁回来执行这个回调函数那?主进程?线程?还是让任务绑定的协程?
  2. 如果是前两个那么就会变成一个同步的代码,可不可以把这部分代码做成异步的那?

我带着这个问题,问了我的度娘,我得到的回答是:

  1. 如果是进程调用,回调函数就返回到调用的进程中执行。

  2. 如果是线程调用,回调函数就返回调用的线程中执行。如果是多个线程的话,那个线程 空闲那个线程执行。

    # -*- coding:utf-8 -*-
    import asyncio
    import time
    
    async def test_func(n):
        print(f'test function {n} start !')
        await asyncio.sleep(2)
        print(f'test function {n} end !')
        return n
    
    
    def test_call_back(task):
        print(f'test_call_back { task.result() } start !')
        time.sleep(2)  # 模拟io阻塞
        print(f'test_call_back { task.result() } end !')
    
    
    pool = ThreadPoolExecutor(5)
    tasks = []
    
    for i in range(3):
        task = asyncio.ensure_future(test_func(i))
        task.add_done_callback(test_call_back)
        tasks.append(task)
    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))
    
    

    通过这个 demo 来验证回答一,执行后发现确实回调函数为同步执行。

    # -*- coding:utf-8 -*-
    import asyncio
    import time
    from concurrent.futures import ThreadPoolExecutor
    
    
    async def test_func(n):
        print(f'test function {n} start !')
        await asyncio.sleep(2)
        print(f'test function {n} end !')
        return n
    
    
    def test_call_back(task):
        print(f'test_call_back { task.result() } start !')
    
        time.sleep(2)
        print(f'test_call_back { task.result() } end !')
    
    
    s = time.time()
    pool = ThreadPoolExecutor(5)
    tasks = []
    
    for i in range(3):
        task = asyncio.ensure_future(test_func(i))
        task.add_done_callback(test_call_back)
        tasks.append(task)
    
    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))
    pool.submit(loop.run_until_complete(asyncio.wait(tasks)))
    
    print(time.time() - s )
    

    通过这个demo 来验证回答2 ,看着好像合理,但是这里只用到了一个线程,所以和进程的效果是一样的。

    而且我查了一下资料,asyncio 好像都是单线程里面使用的,并不能利用多线程调用回调,但是 它有一个方法 run_coroutine_threadsafe() 可以用来做线程间的通信。

那么问题就又回到了原点,怎么才能异步的执行回调那? 我有一次的这样询问 “度娘” 。我得到的回到也不尽如人意,有一些大神封装好了的包,可以做异步的读写,但是因为没有看懂源码,所以放弃了使用。但是大致的思想可以得到,就是在回调函数里面封装线程,示例如下:

# -*- coding:utf-8 -*-
import asyncio
import time
from concurrent.futures import ThreadPoolExecutor


async def test_func(n):
    print(f'test function {n} start !')
    await asyncio.sleep(2)
    print(f'test function {n} end !')
    return n


def test_call_back(task):
    print(f'test_call_back { task.result() } start !')
    pool.submit(time.sleep, 2)
    print(f'test_call_back { task.result() } end !')


s = time.time()
pool = ThreadPoolExecutor(5)
tasks = []

for i in range(3):
    task = asyncio.ensure_future(test_func(i))
    task.add_done_callback(test_call_back)
    tasks.append(task)


loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
pool.submit(loop.run_until_complete(asyncio.wait(tasks)))

print(time.time() - s)

改动的地方很小,就是在造成阻塞的地方从新封装一个函数,然后放到线程池中去执行。这样就可以基本做到了异步的回调。

但是还有没有更优的方法那? 是不是可以用 async 这样的异步函数来执行那?

理论上是可以行的,类比一下线程就知道了,但是 asyncio 模块中并没有提供给我们一个异步操作文件的方法,如果想要使用协程操作文件,提高速度的话,需要自己用协程封装一个,因为我能力有限没有办法实现,那么情歌文大神加油吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值