How can I wrap a synchronous function in an async coroutine?

后端 未结 4 553
南笙
南笙 2021-01-30 08:28

I\'m using aiohttp to build an API server that sends TCP requests off to a seperate server. The module that sends the TCP requests is synchronous and a black box for my purposes

相关标签:
4条回答
  • 2021-01-30 09:09

    Not sure if too late but you can also use a decorator to do your function in a thread. ALTHOUGH, note that it will still be non-coop blocking unlike async which is co-op blocking.

    def wrap(func):
        from concurrent.futures import ThreadPoolExecutor
        pool=ThreadPoolExecutor()
        @wraps(func)
        async def run(*args, loop=None, executor=None, **kwargs):
            if loop is None:
                loop = asyncio.get_event_loop()
            future=pool.submit(func, *args, **kwargs)
            return asyncio.wrap_future(future)
        return run
    
    0 讨论(0)
  • 2021-01-30 09:19

    You can use a decorator to wrap the sync version to an async version.

    import time
    from functools import wraps, partial
    
    
    def wrap(func):
        @wraps(func)
        async def run(*args, loop=None, executor=None, **kwargs):
            if loop is None:
                loop = asyncio.get_event_loop()
            pfunc = partial(func, *args, **kwargs)
            return await loop.run_in_executor(executor, pfunc)
        return run
    
    @wrap
    def sleep_async(delay):
        time.sleep(delay)
        return 'I slept asynchronously'
    

    or use the aioify library

    % pip install aioify
    

    then

    @aioify
    def sleep_async(delay):
        pass
    
    0 讨论(0)
  • 2021-01-30 09:26

    Eventually I found an answer in this thread. The method I was looking for is run_in_executor. This allows a synchronous function to be run asynchronously without blocking an event loop.

    In the sleep example I posted above, it might look like this:

    import asyncio
    from time import sleep
    
    async def sleep_async(loop, delay):
        # None uses the default executor (ThreadPoolExecutor)
        await loop.run_in_executor(None, sleep, delay)
        return 'I slept asynchronously'
    

    Also see the following answer -> How do we call a normal function where a coroutine is expected?

    0 讨论(0)
  • 2021-01-30 09:31

    Maybe someone will need my solution to this problem. I wrote my own library to solve this, which allows you to make any function asynchronous using a decorator.

    To install the library, run this command:

    $ pip install awaits
    

    To make any of your functions asynchronous, just add the @awaitable decorator to it, like this:

    import time
    import asyncio
    from awaits.awaitable import awaitable
    
    @awaitable
    def sum(a, b):
      # heavy load simulation
      time.sleep(10)
      return a + b
    

    Now you can make sure that your function is really asynchronous coroutine:

    print(asyncio.run(sum(2, 2)))
    

    "Under the hood" your function will be executed in the thread pool. This thread pool will not be recreated every time your function is called. A thread pool is created once and accepts new tasks via a queue. This will make your program run faster than using other solutions, because the creation of additional threads is an additional overhead.

    0 讨论(0)
提交回复
热议问题