Python/Advanced

[Python 비동기] (2) Generator, Async/await 코루틴 코드 비교

yubi5050 2023. 2. 9. 16:58

이전 글에서는 Python의 코루틴, Async/await 용어 등에 대해 이해해보았다. (개념과 설명에 대한 이해는 이전글 참조)

https://yubi5050.tistory.com/234

 

[Python 비동기] (1) 코루틴, Async/await 용어 이해

Python 비동기 배경 1. python 은 동기 기반으로 설계된 언어로 대부분의 내장 API들이 동기로 작동 2. python 의 비동기 방식(코루틴)들은 Generator를 이용해 비동기 코드를 작성함 3. python 3.4에 asyncio 라

yubi5050.tistory.com

 

이번 글에서는 Python 코루틴 구현을 위한 Generator와 asyncio 라이브러리의 async/await 구문을 구현 및 비교해본다.

 

코드 예제 - Generator

generator는 yield 구문을 이용해 class generator 방식의 코루틴이 동작 된다.

next로 generator 객체를 호출시 yield의 값들이 반환되며, 설정된 iterator의 갯수가 초과되면 Stopiteration이 발생한다.

 

Generator를 이용한 Coroutine

- yield 상태가 되어, 실행이 일시 정지 되어 있는 generator에 값을 전달하여 실행하고 결과를 받음

# generator_ex1.py
def test1():
    print('print 1')
    yield 1
    print('print 2')
    yield 2

def test2():
    for i in range(10):
        yield i * 2

if __name__ == "__main__":	
    # case 1
    gen1 = test1()
    print(type(gen1)) # class 'generator'
    print('=== ', next(gen1))
    print('=== ', next(gen1))
    print()

    # case 2
    gen2 = test2()
    print(type(gen2)) # class 'generator'
    for _ in range(10+1):
        print(next(gen2), end = ' ') # stop iteration 에러 발생

 

generator example

 

코드 예제 - async/await

awyncio 라이브러리의 async/await 구문을 이용해 코루틴을 작성한다.

 

단순 await를 사용했을 때와 태스크 오브젝트를 생성하는 create_task 방식으로 나누어서 비교해본다.

- main1() : await 만 단순 사용하며 say_after(1), say_after(2)가 순차적으로 실행 => 총 3초가 소요됨

- main2() : create_task()로 task object를 만들어 say_after(1), say_after(2) 동시에 실행 => 총 2초가 소요됨

- asyncio.run() 함수 : 현재의 스레드에 event_loop를 설정해, 넘어오는 Coroutine 객체들을 Task로 만들어 실행하고, 해당 Task가 완료되면 event_loop를 해지

import asyncio
import time

# await asyncio.sleep(1)  # await : 기다리게 하기.
async def say_after(delay):
    await asyncio.sleep(delay)

# 1. create_task 사용 없이 실행 - 순차 실행 : 3초 소요
async def main1():
    print(f"1. started at {time.strftime('%X')}")
    await say_after(1)
    await say_after(2)
    print(f"1. finished at {time.strftime('%X')}")

# 2. create_task로 만들고 동시에 실행 - 동시 실행 : 2초 소요
async def main2():
    task1 = asyncio.create_task(
        say_after(1))

    task2 = asyncio.create_task(
        say_after(2))

    print(f"2. started at {time.strftime('%X')}")
    await task1
    await task2
    print(f"2. finished at {time.strftime('%X')}")

if __name__ == "__main__":
    print('=== main1 test ===')
    asyncio.run(main1())

    print('=== main2 test ===')
    asyncio.run(main2())

 

async/await example

 

 

async/await의 event loop 플로우

최초 sync 함수(위 코드에선 실제 .py의 main 함수) 에서 run()을 통해 event_loop를 활성화하고, 실행되는 코루틴 객체들을 병렬적으로 실행하고, 완료되면 event_loop를 닫는다. 

 

출처:https://it-eldorado.tistory.com/159

 

asyncio 라이브러리를 통해 coroutine을 구현시 아래 두 경우는 같은 역할을 수행한다.

asyncio.run()
loop = asyncio.get_event_loop()
loop.run_until_complete(coroutine_method())
loop.close()

 

참고 문헌