🚨 더 구체적인 내용은 아래 링크에 아주 친절하게 잘 정리되어 있으니 꼭 링크가서 읽어주세요!!
https://it-eldorado.tistory.com/159?category=749661
이전 글에서는 이벤트 루프가 태스크들을 "동시적"으로 실행한다고 설명하였다.
그러나 사실 asyncio.run()
함수는 기본적으로 하나의 태스크만을 생성하여 실행한다.
따라서 코루틴 체인 과정에서 추가적인 태스크를 생성하여 실행하지 않았다면 현재의 태스크가 중단되었을 때 이벤트 루프는 실행시킬 다른 태스크가 없게 된다.
태스크가 한 개라면 동시적인 실행을 하는 것이 애초에 말이 되지 않는 것이다.
따라서 동시적인 실행을 위해서는 asyncio.create_task()
함수를 호출함으로써 태스크를 추가로 생성하여 실행해야 한다.
이 함수를 호출할 때 코루틴 객체를 인자로 넘기면 해당 코루틴 객체를 이용하여 태스크 객체를 생성하고 이를 반환한다.
그리고 앞서 말했듯 태스크 객체가 생성되면 해당 태스크 객체가 나타내는 태스크의 실행이 이벤트 루프에 의해 즉시 에약된다.(즉시 실행이 아니다.)단, 이함수는 3.7 버전 이상의 Python에서만 사용할 수 있기 때문에, 그 이전 버전에서는 asyncio.ensure_future()
함수를 사용해야 한다.
다음으로, 모든 퓨처 객체(태스크 객체를 포함한다.)들이 완료 상태가 될 때까지 기다리는 함수가 asyncio.gather()
이다. 이 함수는 인자로 여러개의 Awaitable한 객체들을 받을 수 있는데, 만약 코루틴 객체를 받으면 이는 자동으로 태스크 객체로 래핑이 된다. 따라서 사실상 퓨처 객체(태스크 객체 포함)만 넘어간다고 생각해도 된다.
그리고 모든 퓨처 객체들이 완료상태가 되면, 그것들의 결과 값을 "리스트 형태"로 반환한다.
그 순서는 인자로 넘긴 순서와 동일하다. 이 함수는 await
키워드 뒤에서 호출될 수 있는 코루틴의 일종이다.
예시 코드를 통해 한 번 알아보자.
import asyncio
import time
async def sleep(sec):
await asyncio.sleep(sec)
return sec
async def main():
sec_list = [1, 2]
tasks = [asyncio.create_task(sleep(sec)) for sec in sec_list]
tasks_results = await asyncio.gather(*tasks)
return tasks_results
start = time.time()
loop = asyncio.get_event_loop()
result = loop.run_until_complete(main())
loop.close()
end = time.time()
print('result : {}'.format(result))
print('total time : {0:.2f} sec'.format(end - start))
# 출력 결과
# result : [1, 2]
# total time : 2.00 sec
위 예시코드의 실행흐름을 파악해보면 다음과 같다.
loop.run_until_complete()
함수를 통해 Task0이 실행이 되고 이로 인해 main() 코루틴이 예약된다.main()
코루틴은 asyncio.create_task()
함수를 통해 (sec_list가 총 2개이니)Task1, Task2 객체를 생성하고 실행을 예약한다.asyncio.gather()
코루틴은 Task1 객체를 await한다.sleep(1)
코루틴을 실행하고, 인자값을 받아 sleep 함수 내 asyncio.sleep(1)
코루틴을 실행한다.asyncio.sleep(1)
코루틴은 Future1 객체를 만들고 1초 뒤에 Future1 객체의 결과 값이 갱신되도록 이벤트 루프에 예약을 건 뒤 Future1 객체를 await한다.asyncio.sleep(2)
코루틴을 실행한다.asyncio.sleep(2)
코루틴은 Future2 객체를 만들고 2초뒤에 Future2 객체의 결과 값이 갱신되도록 이벤트 루프에 예약을 건뒤 Future2 객체를 await한다.asyncio.sleep(1)
코루틴으로 돌아가서 실행이 중단되었던 부분부터 실행을 재개한다.asyncio.sleep(1)
코루틴이 리턴하고, sleep(1) 코루틴도 리턴한다. 이때 반환 값은 1이다.asyncio.gather()
코루틴으로 돌아가서 실행이 중단되었던 부분부터 실행을 재개한다.asyncio.gather()
코루틴은 Task1 객체의 결과 값을 저장하고, Task2 객체를 await 한다.loop.run_until_complete()
의 실행이 완료되고 이벤트 루프를 닫는다.