Python3.5Async/await与真实代码示例

Python3.5Async/await与真实代码示例,python,asynchronous,async-await,python-3.5,python-asyncio,Python,Asynchronous,Async Await,Python 3.5,Python Asyncio,我已经阅读了大量关于Python 3.5异步/等待的文章和教程。我不得不说我很困惑,因为有些人使用get_event_loop()并运行_直到_complete(),有些人使用sure_future(),有些人使用asyncio.wait(),还有一些人使用call_soon() 看起来我有很多选择,但我不知道它们是否完全相同,或者有些情况下使用循环,有些情况下使用wait() 但问题是,所有示例都使用asyncio.sleep()模拟返回等待对象的真正慢速操作。一旦我试图用这行代码替换一些真正

我已经阅读了大量关于Python 3.5异步/等待的文章和教程。我不得不说我很困惑,因为有些人使用get_event_loop()并运行_直到_complete(),有些人使用sure_future(),有些人使用asyncio.wait(),还有一些人使用call_soon()

看起来我有很多选择,但我不知道它们是否完全相同,或者有些情况下使用循环,有些情况下使用wait()

但问题是,所有示例都使用
asyncio.sleep()
模拟返回等待对象的真正慢速操作。一旦我试图用这行代码替换一些真正的代码,整个事情就失败了。上面写的方法之间的区别是什么?我应该如何运行一个还没有准备好异步/等待的第三方库。我确实使用Quandl服务获取一些股票数据

 import asyncio
 import quandl

 async def slow_operation(n):
     # await asyncio.sleep(1) # Works because it's await ready.
     await quandl.Dataset(n) # Doesn't work because it's not await ready.


 async def main():
     await asyncio.wait([
         slow_operation("SIX/US9884981013EUR4"),
         slow_operation("SIX/US88160R1014EUR4"),
     ])

 # You don't have to use any code for 50 requests/day.
 quandl.ApiConfig.api_key = "MY_SECRET_CODE"

 loop = asyncio.get_event_loop()
 loop.run_until_complete(main())

我希望你能明白我是多么迷茫,我希望并行运行是多么简单。

如果第三方库与
async/await
不兼容,那么显然你无法轻松使用它。有两种情况:

  • 假设库中的函数是异步的,它会给您一个回调,例如

    def fn(..., clb):
        ...
    
    因此,您可以:

    def on_result(...):
        ...
    
    fn(..., on_result)
    
    在这种情况下,您可以将此类函数包装到asyncio协议中,如下所示:

    from asyncio import Future
    
    def wrapper(...):
        future = Future()
        def my_clb(...):
            future.set_result(xyz)
        fn(..., my_clb)
        return future
    
    import asyncio
    import time
    from concurrent.futures import ThreadPoolExecutor
    
    # Initialize 10 threads
    THREAD_POOL = ThreadPoolExecutor(10)
    
    def synchronous_handler(param1, ...):
        # Do something synchronous
        time.sleep(2)
        return "foo"
    
    # Somewhere else
    async def main():
        loop = asyncio.get_event_loop()
        futures = [
            loop.run_in_executor(THREAD_POOL, synchronous_handler, param1, ...),
            loop.run_in_executor(THREAD_POOL, synchronous_handler, param1, ...),
            loop.run_in_executor(THREAD_POOL, synchronous_handler, param1, ...),
        ]
        await asyncio.wait(futures)
        for future in futures:
            print(future.result())
    
    with THREAD_POOL:
        loop = asyncio.get_event_loop()
        loop.run_until_complete(main())
    
    (使用
    future.set_异常(exc)
    on异常)

    然后,您可以使用
    await
    在一些
    async
    函数中调用该包装器:

    value = await wrapper(...)
    
    请注意,
    await
    适用于任何
    Future
    对象。您不必将
    wrapper
    声明为
    async

  • 如果库中的函数是同步的,那么可以在单独的线程中运行它(可能需要使用一些线程池)。整个代码可能如下所示:

    from asyncio import Future
    
    def wrapper(...):
        future = Future()
        def my_clb(...):
            future.set_result(xyz)
        fn(..., my_clb)
        return future
    
    import asyncio
    import time
    from concurrent.futures import ThreadPoolExecutor
    
    # Initialize 10 threads
    THREAD_POOL = ThreadPoolExecutor(10)
    
    def synchronous_handler(param1, ...):
        # Do something synchronous
        time.sleep(2)
        return "foo"
    
    # Somewhere else
    async def main():
        loop = asyncio.get_event_loop()
        futures = [
            loop.run_in_executor(THREAD_POOL, synchronous_handler, param1, ...),
            loop.run_in_executor(THREAD_POOL, synchronous_handler, param1, ...),
            loop.run_in_executor(THREAD_POOL, synchronous_handler, param1, ...),
        ]
        await asyncio.wait(futures)
        for future in futures:
            print(future.result())
    
    with THREAD_POOL:
        loop = asyncio.get_event_loop()
        loop.run_until_complete(main())
    
  • 如果出于任何原因不能使用线程,那么使用这样的库只会使整个异步代码毫无意义


    但是请注意,将同步库与异步一起使用可能是个坏主意。您不会得到太多,但会使代码复杂化。

    您可以从中查看以下简单的工作示例。顺便说一下,它返回一个值得读取的字符串:-)


    @grafa我的错,我没有测试代码。我现在已经修好了,并且简化了很多。实际上Python有一个整洁的
    .run\in\u executor
    函数,负责返回值和异常处理。无需手动执行。@grafa请注意,如果您安排的任务超过线程池的大小,则这些任务将排队等待线程。@grafa还请记住,之后必须关闭executor。请在此处阅读更多信息:@grafa首先安排调用,然后可以使用
    asyncio.wait()
    等待所有调用。最后,您可以通过调用
    future.result()
    来循环futures并检索结果。请参阅更新的代码。请注意,
    .run\u in\u executor
    已经返回了一个未来的对象,不需要包装它。非常感谢。每个想法都有效,我想我知道整个事情。您是这个星球上第一个提供了如何将Python 3.5 async/await用于经典非异步任务的复杂示例的人。再次感谢!