티스토리 뷰

[비동기 프로그래밍: Async]



웹페이지를 불러오는 함수가 있다. 1000페이지를 불러오기 위해 시작하면 한 페이지 불러오고... 또, 한 페이지 불러오고... 또, 한페이지... 불러올 것이다.

이보다 효율적으로 페이지를 불러오기 위해서는

첫째, 1000페이지를 동시에 불러온다.

둘째, 한 페이지를 로딩하면서 기다리는 동안 다른 페이지를 불러온다.

두 가지 방식이 비슷해 보여도 일 처리하는 방식은 다르다. 1번은 멀티스레드 방식이고 2번은 비동기 프로그래밍 방식이다.

이 두 개념의 차이를 이해해보자.

한 회사원이 있다. 전화도 받아야 하고 결재 올려 승인을 받아야 하고 메일도 보내야 하고 고객과 만나 업무도 처리해야 한다.

가끔 바쁠 때 "몸이 열 개면 좋겠다"라고 말한다. 멀티 스레드는 실제 몸이 열 개이다. 그래서 전화 받는 공무원, 메일 보내는 공무원, 민원 처리하는 공무원이 각각 존재한다. 당연히 빠르다. 하지만 전화를 끊은 공무원은 시간이 남아도 다른 일을 도와주지 않는다. 그냥 논다.

비동기식 프로그래밍은 "몸이 열 개면 좋겠다"라고 생각만 하고 결국 혼자 모든 일을 다 한다. 대신 엄청나게 똑똑하고 몸이 빠르다.

상사에게 결재 올려 승인이 날 때까지 기다리지 않고 전화도 받고 메일도 보낸다. 이 분은 '일머리'를 안다. 똑똑하고 체계적이고 계획적이다. 열 명의 공무원 부럽지 않다.

이처럼 비동기식 프로그래밍을 하기 위해서는 한번 주면 받을 때까지 기다리기만 하는 함수를 발전시켜 멀티태스킹이 가능한 함수로 만들어야 한다. 이런 멀티태스킹이 가능한 함수를 특별히 코루틴(Coroutine)이라 부른다.

파이썬 3.5이상에서는 Coroutine을 좀 더 계획적이고 더 나은 환경을 제공한다. 파이썬에 내장된 asyncio 패키지를 사용하는 것이다.

비동기식으로 블로그 포스트를 크롤링 하는 예제를 살펴보자. 아래 코드를 실행하면 여러 페이지를 로딩될 때까지 기다리지 않고 바로 다음 페이지를 로딩하여 아주 빠르게 모든 페이지를 불러온다.



[다수의 io 요청을 동시에 수행하는 루틴]


import asyncio
import random


async def method_io(delay=1):
req = await loop.run_in_executor(None, random.randrange, 1, 10)
await asyncio.sleep(delay)
print(req)
return req


async def main():
fts = [asyncio.ensure_future(method_io()) for _ in range(5)]
results = await asyncio.gather(*fts)
print(results)


if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()
print("end")


async 에선 함수 앞에 async를 붙여 주어야 코루틴 함수가 된다.


비동기식 프로그래밍에선 함수가 아닌 코루틴 이라고 부른다.


fts 변수들은 future 객체로 만들어서 넣었다. future 는 계획서 이다.


그래서 아직 실행되는 것은 아니다.


await asyncio.gather() 는 계획서를 실행하고 그 결과를 리턴 받을 수도 있다.



req = await loop.run_in_executor(None, random.randrange, 1, 10)


await 는 코루틴을 실행하는 예약어이다. 이것을 실행해야 다른 코루틴을 실행할수 있다.


원래 파이썬에 있는 모든 함수가 비동기식으로 작동될 수 있는 것은 아니다.


코루틴으로 만들어지지 않았기 때문이다. 


따라서 지원하는 함수만 코루틴으로 실행될 수 있다.


loop.run_in_executro 함수를 통해서 일반 함수도 코루틴 처럼 실행 될수 있다.








댓글