美文网首页
Python3:让不支持async的函数实现异步操作

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

作者: 沧海一声笑的DIY宇宙 | 来源:发表于2019-11-09 12:49 被阅读0次

    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改造成非阻塞,在一定的场合下还是能工作的不错。

    相关文章

      网友评论

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

          本文链接:https://www.haomeiwen.com/subject/dblbbctx.html