Python3:让不支持async的函数实现异步操作

2019-11-09  本文已影响0人  沧海一声笑的DIY宇宙

asyncio是Python3引入的异步IO功能,主要是在语言层面上解决IO阻塞的问题。

常规的用法是在主函数中运行run_until_complete这个函数。但是有的时候,代码中使用的第三方库也需要在主函数中运行自己的循环函数,比如PyQt。那么run_until_complete这个函数就无法调用了。

我尝试了下创建一个新的loop对象,在线程中调用loop对象的循环函数,解决了这个问题。

# 创建一个loop循环

new_loop = asyncio.new_event_loop()

# 定义线程函数

def Loop(loop):

    while True:

        try:

            loop.run_forever()    # 这里调用了asynicio的循环函数

        except KeyboardInterrupt:    # 按ctrl-c能退出

            loop.close()

# 创建一个线程,在线程里面执行 run_forever

t = threading.Thread(target = Loop, args=(new_loop,))

t.daemon = True

t.start()

while True: # 这里是模拟其他库的阻塞函数

    pass

在上面的代码里面,创建好了异步调用的流程,接下来是如何调用不支持asyncio的函数。拿requests来举例吧。

requests是python下常用的web请求库。我们知道http请求是很费时的,调用一次几十到几百毫秒的阻塞。

现在实现一个异步的请求:

def PostFinishTask(action_id):

    asyncio.run_coroutine_threadsafe(PostFinishTaskAsync(action_id), new_loop)

用到的变量是刚才创建的new_loop对象,表示本次异步调用会在刚创建的线程里面执行,不会阻塞主线程。

async def PostFinishTaskAsync(action_id):

    payload = { 'action_id': action_id }    # 模拟的一些网络参数

    def Do_post(): # 为了解决传参的问题,定义一个内部函数

        return requests.post("https://some_call_url", data = payload)

    future = new_loop.run_in_executor(None, Do_post) 

    response = await future # 等待调用完成

这个PostFinishTaskAsync函数才是真正干活的函数。首先加上async,然后为了解决run_in_executor无法给它回调的函数传多个参数,额外的定义了一个Do_post函数用来绕过。

经过以上改造,requests变成了非阻塞的调用了。

本文解决了两个问题,一个是如何自定义一个asyncio的loop循环。另一个是如何把阻塞函数改成非阻塞。虽然Python是一个很慢的语言,但是将IO改造成非阻塞,在一定的场合下还是能工作的不错。

上一篇 下一篇

猜你喜欢

热点阅读