파이썬을 이용해 비동기 파트를 다루면서 다음 용어에 대해 많이 접하게 되었다.
한 번쯤 들어봤을 코루틴, 제너레이터와 이터레이터에 대해 정리해보았다.
파이썬에서 여러 작업을 동시에 병렬 처리하기 위해 비동기(Asynchronous) 함수를 사용한다.
async def cr_func():
print('test')
이렇게 async 키워드로 만든 비동기 함수를 코루틴(Coroutine) 또는 코루틴 함수라고 한다. 그렇다면, 이 코루틴 함수는 언제 어떻게 사용하는 것일까?
async def cr_func():
print('test')
cr_func()
### ----- Result ----- ###
RuntimeWarning: coroutine 'cr_func' was never awaited
### ------------------- ###
cr_func 이라는 이름의 코루틴 함수를 선언하고 호출하자 다음과 같은 에러가 나타났다.
async 키워드로 선언된 코루틴 함수는 일반 함수 호출 방식과는 다르며, 다음과 같이 실행한다.
import asyncio
async def cr_func(): # 코루틴 함수 선언
print('Coroutine Function!')
loop = asyncio.get_event_loop() # asyncio 모듈의 event loop 객체 생성
loop.run_until_complete(cr_func()) # cr_func 함수가 완전히 종료될 때까지 대기
loop.close() # event loop 종료
### ----- Result ----- ###
Coroutine Function!
### ------------------- ###
📌 asyncio 모듈의 get_event_loop를 얻어와서 run_until_complete의 인자로 코루틴 함수를 전달하여 실행한다.
run_until_complete 함수는 인자로 받은 함수가 완전히 종료될 때까지 이벤트 루프를 실행하게 된다.
이와 같이, async로 선언된 코루틴을 "네이티브 코루틴" 이라고 한다.
다른 함수에서 이 코루틴 함수를 호출할 때는 어떻게 할까?
import asyncio
async def cr_func():
print('test')
async def main():
await cr_func()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()
코루틴 함수 생성 방식은 동일하나, 코루틴 함수를 호출하고자 하는 함수에서 await 키워드를 붙여줘야 한다.
await = 말 그대로 cr_func()함수가 완료되기를 기다린 후 결과를 가져온다는 의미이다.
응답을 받을 때까지 아무것도 하지 않고 기다린다는 것이 아니라 해당 함수가 완료될 때까지 다른 작업을 하면서 완료를 기다린다는 의미이다.
일반적으로 사용하는 네이티브 코루틴 외의 또 다른 방식인 제너레이터 기반 코루틴에 대해 정리해보았다.
다음은 yield
키워드를 통해 메인 루틴과 서브 루틴간에 값을 이동하며 실행의 지연 및 재개를 보여주는 예제이다.
def cr_gen():
total = 0
while True: # 코루틴을 계속 유지하기 위해 무한 루프 사용
num = yield total # total 발생 및 num에 값 저장
total += num
cr = cr_gen()
print(next(cr)) # 처음 yield 까지 실행
print(cr.send(3)) # num에 3 저장 후 다음 yield 까지 실행
print(cr.send(2)) # num에 2 저장 후 다음 yield 까지 실행
""" 실행결과
0
3
5
"""
📌이와 같이 코루틴 바깥에서 보낸 값을 yield 변수(여기서는 num)에 받아 저장한 후, 다음 루틴을 실행한다.
정리하면, 핵심은 "코루틴은 yield에서 함수 중간에 대기한 다음에 메인 루틴을 실행하다가 다시 코루틴을 실행한다"는 것이다.