2014-10-24 6 views
4

Python 3 asyncio 프레임 워크를 사용하여 주기적으로 실행되는 패턴 (실제 sleep/delay는 생략하지 않음)을 평가하고 있으며 두 가지 코드가 서로 다르게 동작합니다 왜 그런지 설명 할 수 없습니다. 첫 번째 버전은 yield from을 호출하여 예상대로 반복적으로 약 1000 회의 반복으로 스택을 소모합니다. 두 번째 버전에서는 coroutine을 재귀 적으로 호출하지만 실제 이벤트 루프 실행을 asyncio.async으로 위임하고 스택을 소모하지 않습니다. 스택이 두 번째 버전에서 사용되지 않는 이유를 자세히 설명 할 수 있습니까? 이 코 루틴을 실행하는 두 가지 방법의 차이점은 무엇입니까?파이썬 3 asyncio - vs asyncio.async 스택 사용량에서 yield

첫 번째 버전 (발 수율)

@asyncio.coroutine 
def call_self(self, i): 
    print('calling self', i) 
    yield from self.call_self(i + 1) 

번째 버전 (asyncio.async)

@asyncio.coroutine 
def call_self(self, i): 
    print('calling self', i) 
    asyncio.async(self.call_self(i + 1)) 
+0

실행 해 보셨습니까? 두 번째 버전은 몇 번만 인쇄합니다 ('loop.run_until_complete (self.call_self (0))'로 실행하면 한 번만 출력 될 것입니다) – jfs

+0

@JFSebastian 실제로 실행하려고 시도 했으므로 run_until_complete하지만 run_forever로 실행하지 마십시오. 이 두 가지 모두 run_forever로 무한히 인쇄하려고 시도하지만, 첫 번째는 스택을 소모하고 두 번째 스택은 영원히 (2 백만 +) 실행됩니다. –

+0

그런 다음 run_until_complete()를 사용하여 두 변종을 실행하려고 시도합니다. 결과 기다리는 것과 코 루틴을 예약하는 것의 차이점을 보여줍니다. – jfs

답변

10

첫번째 예는, yield from, 실제로는 블록의 재귀 호출까지 call_self 각 인스턴스를 사용 ~ call_self로 돌아갑니다. 즉, 스택 공간이 부족할 때까지 호출 스택이 계속 커지는 것을 의미합니다. 언급했듯이 이것은 분명한 행동입니다.

두 번째 예는 asyncio.async을 사용하여 어디에서도 차단하지 않습니다. 따라서 call_self의 각 인스턴스는 asyncio.async(...)을 실행 한 후 즉시 종료됩니다. 즉, 스택이 무한대로 커지지 않음을 의미합니다. 즉, 스택을 소모하지 않음을 의미합니다. 대신, asyncio.async 스케줄 call_self은 이벤트 루프의 반복에서 asyncio.Task에 랩핑하여 실행됩니다.

여기 Task에 대한 __init__의 :

self._loop.call_soon(self._step)에 대한 호출은 실제로 코 루틴이 실행 만드는 것입니다
def __init__(self, coro, *, loop=None): 
    assert iscoroutine(coro), repr(coro) # Not a coroutine function! 
    super().__init__(loop=loop) 
    self._coro = iter(coro) # Use the iterator just in case. 
    self._fut_waiter = None 
    self._must_cancel = False 
    self._loop.call_soon(self._step) # This schedules the coroutine to be run 
    self.__class__._all_tasks.add(self) 

. 그것은 non-blocking 방식으로 일어나기 때문에, call_self의 호출 스택은 Task 생성자에 대한 호출을 넘어 결코 성장하지 않습니다. 그런 다음 call_self의 다음 인스턴스는 다음 반복 (이전 call_self이 반환 되 자마자 시작되는 이벤트 루프)에 의해 시작되고 이전 call_self 인스턴스의 컨텍스트 외부에서 완전히 벗어납니다.

+1

+1. 'async()'가 코 루틴 (coroutine)을 스케쥴하는데 (소스를 파낼 필요가 없음) 문서화되어있다. 중요한 세부 사항 :'asyncio.coroutine' 데코레이터는'call_self'를 생성자로 변환합니다. 그렇지 않으면'async (call_self())'는 async()가 무엇이든간에 스택을 소모합니다. – jfs