asyncio.ensure_future 대 BaseEventLoop.create_task 대 단순 코 루틴?


96

asyncio에 대한 몇 가지 기본 Python 3.5 자습서가 다양한 방식으로 동일한 작업을 수행하는 것을 보았습니다. 이 코드에서 :

import asyncio  

async def doit(i):
    print("Start %d" % i)
    await asyncio.sleep(3)
    print("End %d" % i)
    return i

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    #futures = [asyncio.ensure_future(doit(i), loop=loop) for i in range(10)]
    #futures = [loop.create_task(doit(i)) for i in range(10)]
    futures = [doit(i) for i in range(10)]
    result = loop.run_until_complete(asyncio.gather(*futures))
    print(result)

futures변수 를 정의하는 위의 세 가지 변형 모두 동일한 결과를 얻습니다. 내가 볼 수있는 유일한 차이점은 세 번째 변형의 경우 실행 순서가 잘못되었다는 것입니다 (대부분의 경우 중요하지 않음). 다른 차이점이 있습니까? 가장 단순한 변형 (코 루틴의 일반 목록)을 사용할 수없는 경우가 있습니까?

답변:


116

실제 정보 :

이 목적을 위해 Python 3.7부터 asyncio.create_task(coro)고급 기능 이 추가되었습니다 .

대신 coroutimes에서 작업을 만드는 다른 방법을 사용해야합니다. 그러나 임의의 awaitable에서 작업을 생성해야하는 경우 asyncio.ensure_future(obj).


이전 정보 :

ensure_future vs create_task

ensure_future에서 만드는 방법 Task입니다 coroutine. 인수에 따라 다른 방식으로 작업을 생성합니다 ( create_task코 루틴 및 미래형 객체 에 대한 사용 포함 ).

create_task의 추상 방법입니다 AbstractEventLoop. 다른 이벤트 루프는이 기능을 다른 방식으로 구현할 수 있습니다.

ensure_future작업을 생성하는 데 사용해야 합니다. create_task자체 이벤트 루프 유형을 구현하려는 경우에만 필요 합니다.

업데이트 :

@ bj0 은이 주제에 대한 Guido의 답변 을 지적했습니다 .

요점은 ensure_future()코 루틴 또는 a Future(후자는 Task의 하위 클래스이기 때문에 a가 포함됨 Future)가 될 수 있고, Future(아마 유일한 것에 대해) 정의 된 메서드를 호출 할 수 있기를 원하는 경우입니다. 유용한 예 cancel()). 이미 a Future(또는 Task) 이면 아무 작업도 수행하지 않습니다. 그것은 코 루틴 인 경우는 A의 그것 Task.

코 루틴이 있다는 것을 알고 있고이를 예약하려는 경우 사용할 올바른 API는 create_task(). 호출해야하는 유일한 시간 ensure_future()은 코 루틴 또는 a를 허용하는 API (대부분의 asyncio 자체 API와 같이)를 제공 Future하고 Future.

이후 :

결국 나는 그것이 ensure_future()거의 필요하지 않은 기능에 대한 적절하게 모호한 이름 이라고 여전히 믿습니다 . 코 루틴에서 작업을 생성 할 때 적절한 이름의 loop.create_task(). 아마도 그것에 대한 별칭이 있어야 asyncio.create_task()할까요?

놀랍습니다. ensure_future함께 사용 하는 주된 동기 는 루프의 구성원과 비교하여 더 높은 수준의 기능이라는 것이 었습니다 create_task(토론 에는 추가 asyncio.spawn또는 같은 아이디어 가 포함되어 있습니다asyncio.create_task ).

또한 Awaitable코 루틴 만 처리 할 수있는 것이 아니라 모든 것을 처리 할 수있는 범용 함수를 사용하는 것이 매우 편리하다고 생각합니다 .

그러나 Guido의 대답은 분명합니다. "코 루틴에서 작업을 만들 때는 적절한 이름을 사용해야합니다. loop.create_task()"

코 루틴을 작업에 래핑해야하는 경우

코 루틴을 태스크에 래핑-이 코 루틴을 "백그라운드에서"시작하는 방법입니다. 예를 들면 다음과 같습니다.

import asyncio


async def msg(text):
    await asyncio.sleep(0.1)
    print(text)


async def long_operation():
    print('long_operation started')
    await asyncio.sleep(3)
    print('long_operation finished')


async def main():
    await msg('first')

    # Now you want to start long_operation, but you don't want to wait it finised:
    # long_operation should be started, but second msg should be printed immediately.
    # Create task to do so:
    task = asyncio.ensure_future(long_operation())

    await msg('second')

    # Now, when you want, you can await task finised:
    await task


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

산출:

first
long_operation started
second
long_operation finished

차이를 느끼기 위해 asyncio.ensure_future(long_operation())로 대체 할 수 있습니다 await long_operation().


3
Guido에 따르면, create_task일반적으로 필요하지 않은 작업 객체가 정말로 필요한 경우 사용해야합니다. github.com/python/asyncio/issues/477#issuecomment-268709555
bj0

@ bj0이 링크에 감사드립니다. 이 토론의 정보를 추가하여 답변을 업데이트했습니다.
Mikhail Gerasimov

않는 ensure_future자동으로 생성 추가합니다 Task메인 이벤트 루프?
AlQuemist

@AlQuemist 모든 코 루틴, 미래 또는 작업은 자동으로 일부 이벤트 루프에 바인딩되어 나중에 실행됩니다. 기본적으로 현재 스레드에 대한 현재 이벤트 루프이지만 loop키워드 인수를 사용하여 다른 이벤트 루프를 지정할 수 있습니다 ( ensure_future 서명 참조 ).
Mikhail Gerasimov

2
우리는 필요 @laycat await내에서 msg()두 번째 통화에 이벤트 루프로 제어를 반환 할 수 있습니다. 수신 제어가 시작되면 이벤트 루프가 시작 long_operation()됩니다. ensure_future코 루틴을 시작하여 현재 실행 흐름과 동시에 실행 하는 방법을 보여줍니다 .
Mikhail Gerasimov

45

create_task()

  • 코 루틴을 받아들이고,
  • Task를 반환합니다.
  • 루프의 컨텍스트에서 호출됩니다.

ensure_future()

  • Futures, 코 루틴, awaitable 객체,
  • Task (또는 Future가 전달 된 경우 Future)를 반환합니다.
  • 주어진 arg가 코 루틴이면 create_task,
  • 루프 객체를 전달할 수 있습니다.

보시다시피 create_task가 더 구체적입니다.


async create_task 또는 ensure_future없이 기능

간단한 호출 async함수는 코 루틴을 반환합니다.

>>> async def doit(i):
...     await asyncio.sleep(3)
...     return i
>>> doit(4)   
<coroutine object doit at 0x7f91e8e80ba0>

그리고 gather내부적으로는 ensure_future인수가 미래라는 것을 ( ) 보장하기 때문에 명시 적으로ensure_future 으로 중복됩니다.

비슷한 질문 loop.create_task, asyncio.async / ensure_future 및 Task의 차이점은 무엇입니까?


13

참고 : Python 3.7 에서만 유효합니다 (Python 3.5의 경우 이전 답변 참조 ).

공식 문서에서 :

asyncio.create_task(Python 3.7에 추가됨) ensure_future().


세부 묘사:

이제 Python 3.7 이후에는 두 개의 최상위 래퍼 함수가 있습니다 (유사하지만 다름).

음,이 두 래퍼 함수는 모두 BaseEventLoop.create_task. 유일한 차이점은 ensure_future모든 awaitable객체를 받아들이고 이를 Future로 변환하는 데 도움이되는 것입니다. 또한 event_loop에서 고유 한 매개 변수를 제공 할 수 있습니다 ensure_future. 이러한 기능이 필요한지 여부에 따라 사용할 래퍼를 선택하기 만하면됩니다.


문서화되지 않은 또 다른 차이점이 있다고 생각합니다. 루프를 실행하기 전에 asyncio.create_task를 호출하려고하면 asyncio.create_task가 실행중인 루프를 예상하기 때문에 문제가 발생합니다. 이 경우 asyncio.ensure_future를 사용할 수 있지만 실행중인 루프가 요구 사항이 아니기 때문입니다.
coelhudo

4

예를 들어 세 가지 유형 모두 비동기 적으로 실행됩니다. 유일한 차이점은 세 번째 예제에서 10 개의 코 루틴을 모두 미리 생성하고 함께 루프에 제출했다는 것입니다. 그래서 마지막 하나만 무작위로 출력을 제공합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.