IT박스

"실행 후 잊어 버리기"python async / await

itboxs 2020. 9. 25. 07:40
반응형

"실행 후 잊어 버리기"python async / await


때때로 발생해야하는 중요하지 않은 비동기 작업이 있지만 완료 될 때까지 기다리고 싶지 않습니다. Tornado의 코 루틴 구현에서는 단순히 yield키워드 를 생략하여 비동기 함수를 "실행하고 잊을"수 있습니다 .

나는 파이썬 3.5에서 발표 된 새로운 async/ await구문으로 "실행하고 잊어 버리는"방법을 알아 내려고 노력해 왔습니다 . 예 : 단순화 된 코드 스 니펫 :

async def async_foo():
    print("Do some stuff asynchronously here...")

def bar():
    async_foo()  # fire and forget "async_foo()"

bar()

하지만 bar()실행되지 않고 대신 런타임 경고가 표시됩니다.

RuntimeWarning: coroutine 'async_foo' was never awaited
  async_foo()  # fire and forget "async_foo()"

업데이트 :

교체 asyncio.ensure_futureasyncio.create_task파이썬> = 3.7 그것은 최신의, 더 좋은 방법을 사용하고있는 모든 곳에서 경우 산란 작업에 .


asyncio.Task를 "실행하고 잊어 버리기"

파이썬 문서에 따르면 "백그라운드에서"실행asyncio.Task 하기 위해 코 루틴을 시작할 수 있습니다 . asyncio.ensure_future 함수에 의해 생성 된 작업 은 실행을 차단하지 않습니다 (따라서 함수가 즉시 반환됩니다!). 이것은 귀하가 요청한대로 "실행하고 잊어 버리는"방법처럼 보입니다.

import asyncio


async def async_foo():
    print("async_foo started")
    await asyncio.sleep(1)
    print("async_foo done")


async def main():
    asyncio.ensure_future(async_foo())  # fire and forget async_foo()

    # btw, you can also create tasks inside non-async funcs

    print('Do some actions 1')
    await asyncio.sleep(1)
    print('Do some actions 2')
    await asyncio.sleep(1)
    print('Do some actions 3')


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

산출:

Do some actions 1
async_foo started
Do some actions 2
async_foo done
Do some actions 3

이벤트 루프가 완료된 후 작업이 실행되면 어떻게됩니까?

asyncio는 이벤트 루프가 완료되는 순간 작업이 완료 될 것으로 예상합니다. 따라서 다음으로 변경할 경우 main():

async def main():
    asyncio.ensure_future(async_foo())  # fire and forget

    print('Do some actions 1')
    await asyncio.sleep(0.1)
    print('Do some actions 2')

프로그램이 완료된 후 다음 경고가 표시됩니다.

Task was destroyed but it is pending!
task: <Task pending coro=<async_foo() running at [...]

이벤트 루프가 완료된 후 모든 보류중인 작업기다릴 수있는 것을 방지하려면 :

async def main():
    asyncio.ensure_future(async_foo())  # fire and forget

    print('Do some actions 1')
    await asyncio.sleep(0.1)
    print('Do some actions 2')


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

    # Let's also finish all running tasks:
    pending = asyncio.Task.all_tasks()
    loop.run_until_complete(asyncio.gather(*pending))

작업을 기다리는 대신 종료

Sometimes you don't want to await tasks to be done (for example, some tasks may be created to run forever). In that case, you can just cancel() them instead of awaiting them:

import asyncio
from contextlib import suppress


async def echo_forever():
    while True:
        print("echo")
        await asyncio.sleep(1)


async def main():
    asyncio.ensure_future(echo_forever())  # fire and forget

    print('Do some actions 1')
    await asyncio.sleep(1)
    print('Do some actions 2')
    await asyncio.sleep(1)
    print('Do some actions 3')


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

    # Let's also cancel all running tasks:
    pending = asyncio.Task.all_tasks()
    for task in pending:
        task.cancel()
        # Now we should await task to execute it's cancellation.
        # Cancelled task raises asyncio.CancelledError that we can suppress:
        with suppress(asyncio.CancelledError):
            loop.run_until_complete(task)

Output:

Do some actions 1
echo
Do some actions 2
echo
Do some actions 3
echo

This is not entirely asynchronous execution, but maybe run_in_executor() is suitable for you.

def fire_and_forget(task, *args, **kwargs):
    loop = asyncio.get_event_loop()
    if callable(task):
        return loop.run_in_executor(None, task, *args, **kwargs)
    else:    
        raise TypeError('Task must be a callable')

def foo():
    #asynchronous stuff here


fire_and_forget(foo)

Thank you Sergey for the succint answer. Here is the decorated version of the same.

import asyncio
import time

def fire_and_forget(f):
    def wrapped(*args, **kwargs):
        return asyncio.get_event_loop().run_in_executor(None, f, *args, *kwargs)

    return wrapped

@fire_and_forget
def foo():
    time.sleep(1)
    print("foo() completed")

print("Hello")
foo()
print("I didn't wait for foo()")

Produces

>>> Hello
>>> foo() started
>>> I didn't wait for foo()
>>> foo() completed

참고URL : https://stackoverflow.com/questions/37278647/fire-and-forget-python-async-await

반응형