[Python] 비동기 프로그래밍 정리 1 (코루틴, 제너레이터, async, await)

hodu·2022년 10월 28일
2

asyncio

목록 보기
1/7
post-thumbnail

아래 링크를 읽고 정리한 내용입니다.

🚨 더 구체적인 내용은 아래 링크에 아주 친절하게 잘 정리되어 있으니 꼭 링크가서 읽어주세요!!
https://it-eldorado.tistory.com/159?category=749661


1. 코루틴

asyncio를 이해하기 전, 코루틴에 대해 먼저 알아두어야 한다.

코루틴은 서브루틴과 달리 현재 상태값을 저장하고 메인루틴으로 돌아간 뒤 나중에 호출하게 되면 저장했던 상태를 꺼내서 다음 상태를 진행해주는 것을 의미한다.

일반적인 함수를 서브루틴이라고 부르는데 이는 return을 통해 메인루틴으로 영원히 돌아가며 다시 서브루틴으로 돌아올 수 없다는 것에서 차이점이 있다.

그러면, 코루틴은 어떻게 작성되는걸까? → async를 def앞에 붙여주면 된다. async def

2. 비동기 함수

코루틴은 비동기 함수라고 부른다. 비동기란 어떤 작업을 기다리지 않고 그 시간동안 다음 작업을 진행하는 것을 의미한다. python은 동기로 작성된 언어라 나중에 추가된 코루틴이라는 개념은 비동기 코드를 작성할 수 있게 한다.

3. 제너레이터

본격적으로 async로 들어가기 전, 알아두어야 할 개념이 또있다(…!!)

바로 제너레이터인데, 앞에서 말한 코루틴이 이 제너레이터를 기반으로 구현이 되어있다. 즉, 코루틴은 제너레이터 인 것이다.

왜 제너레이터를 이용하였을까? → yield 키워드를 breakpoint로 삼아 실행이 중단되고 재개될 수 있는 특징을 가지고 있기 때문이다.

파이썬 3.5 버전 이하에서는 코루틴을 작성하기 위해서는 제너레이터를 기반으로 작성해야 했다. 하지만 async라는 개념이 나오고 나서부터는 코루틴을 쉽게 작성할 수 있게되었음.

async는 거창한 것이 아니라 코루틴이자 제너레이터이며 비동기 함수를 작성하기 위한 문법이다.

그러면 async def 즉 비동기 함수는 무엇을 반환할까?

→ “코루틴”객체를 반환한다.

async def coroutine():
    print('coroutine')

coroutine()
# 출력 : <coroutine object async_func at 0x015A9540>
# 주의 : 'coroutine'이 출력되지는 않음(코루틴이 실행되진 않음).

4. await

이제 우리는 async는 비동기 함수인 코루틴을 만들기 위한 키워드인 것 까지 알게되었다. 그러면 async와 같이 다니는 짝꿍인 await는 무엇일까?

바로

또 다른 코루틴을 실행하기 위한 키워드

이다.
이렇게만 보면 무슨말인지 모르겠으니 코드를 살펴보자.

import asyncio, time

async def greeting(delay, print_string):
	await asyncio.sleep(delay)
    print(print_string)
    
async def main():
	print(f'시작 시간 -> {time.ctime()}')
   	await greeting(1, 'Hello!')       # 2
    await greeting(2, 'World!')       # 2
    print(f'끝난 시간 -> {time.ctime()}')
    
asyncio.run(main())                   # 1
# 결과
시작 시간 -> 22:45:32
hello
world
끝난 시간 -> 22:45:35

# 실행시간은 총 3초가 걸린 것을 확인할 수 있다. 왜일까?
  1. asyncio.run(main()) : asyncio 모듈에서 제공하는 run()함수로 async def 함수, 즉 비동기 함수를 실행하게 된다.
  2. await를 통해 새로운 코루틴인 greeting을 실행한다.

여기서 생기는 의문점 🤔

잠깐! 🤚

async는 비동기 함수라고 배웠다. 그러면, Hello!를 출력하는 greeting을 실행함과 동시에 World!를 출력하는 greeting을 실행할 것 같지 않은가? 그러면 2초가 걸려야하는데, 생각했던 것과 달리 3초가 걸린 것을 확인할 수 있을 것이다.

나중에 작성하겠지만, create_task(혹은 ensure_future) 를 사용하지 않고 run만 해주게 된다면 코루틴 객체만 리턴한다.

반면 create_task는 코루틴 객체를 가지고 비동기 작업 객체인 Task를 만들고 실행한다.

즉 위에 있는 코드는 앙꼬없는 찐빵 초코없는 초코칩 쿠키인 셈! 비동기 함수를 작성하긴 했으나 비동기적으로 사용하지 못하고 있는 상태인 것이다!

다시 돌아와서...

  • await는 다시말해 코루틴이 다른 코루틴을 실행하기 위한 예약어이며 제너레이터의 yield from 키워드와 유사하다.
  • await 키워드 뒤에는 "코루틴 객체"를 두거나 __await()__메서드가 구현된 Awaitable한 객체를 넣어주면 된다.
  • 이렇게 되면 동작 방법은 __await()__메서드를 호출하여 제너레이터 객체를 얻고, 이를 통해 해당 제너레이터를 실행하는 방식으로 동작한다.

다음 게시글에서는 Future, Task에 대해 정리해보겠다.

profile
안녕 세계!

0개의 댓글