[Fluent Python] 18장 asyncio를 이용한 동시성

18장 asyncio를 이용한 동시성

동시성을 구조, 병렬성은 실행에 관한 것이다. - 롭 파이크의 Concurrency Is Not Paralleism (It’s Better)

이미리 사이먼(Imre Simon)은 과학에서의 2대 죄악으로 동일한 것을 의미하기 위해 다른 단어를 사용하는 것동일한 단어를 이용해서 여러 가지를 의미하는 것이라고 이야기했다.

asyncio는 파이썬 3.4에서 추가되었고, 3.3과도 호환되지만, yield from 표현식을 많이 사용하므로, 그 이전 버전과는 호환되지 않는다. Trollius 프로젝트는 파이썬 2.6 이후 버전으로 하위 포팅된 프로젝트이다.

스레드와 코루틴 비교

asyncio.Future: 논블로킹 설계

asyncio.Futureconcurrent.futures.Future는 인터페이스가 거의 같지만, 다르게 구현되어 있어 호환되지 않는다.

asyncio.Future가 다른 점은 다음과 같다.

Future, Task, 코루틴에서 생성하기

asyncio.async(coro_or_future, *, loop=None): 코루틴과 Future를 다 받을 수 있아서 통합한다. 코루틴을 넘겨주면, loop.create_task()를 호출해서 Task를 생성한다. 이벤트 룹을 생략하면, asyncio.get_event_loop()를 호출해서 루프 객체를 가져온다.

BaseEventLoop.create_task(coro): 이 메서드는 코루틴을 실행하기 위해서 사용되며, 파이썬 3.4.2 이후로 사용가능하다.

asyncio와 aiohttp로 내려받기

파이썬 3.4에서 asyncio는 TCP와 UDP만 지원한다. HTTP 프로토콜을 사용하기 위해서는 서드파티 패키지를 설치해야 한다.

asyncio를 이용한 예제는 다음 링크와 같다.

asyncio.wait() 코루틴은 Future 객체나 코루틴의 반복형을 받아서, Task 안에 래핑한다. 결국 모든 객체가 Future객체가 되고, 이를 loop.run_until_complete()에 넘겨주어 실행하게 된다. 리턴 값으로 (<실행완료된 Future 셋>, <실행 완료되지 않은 Future 셋>)를 전달 받는데, 일반적으로 두번째 인자는 공집합이므로 _로 할당한다. 하지만 일부 Future객체는 timeoutreturn_when 키워드를 통해 완료 안된 채로 리턴될 수 있다. 자세한 사항은 공식문서를 참조하자.

이 예제에서 블로킹 입출력을 수행하는 이전 코드를 사용할 수 없다. asyncio를 활용하기 위해서는 모든 함수가 yield from를 통해서 제어권을 다시 이벤트 루프에 넘겨주어야 하기 때문이다.

asyncio는 알아야 할 개념도 많고 복잡해 보이지만, 귀도 반 로섬은 ‘실눈을 뜨고 보면서 yield from 키워드가 없는 것처럼 생각’하면 된다고 조언한다.

yield from foo를 사용하면, 이 코드가 있는 현재의 코루틴은 중단되지만, 제어권이 이벤트 루프로 다시 넘어가서 이벤트 루프가 블로킹 되지 않는다. foo가 완료되면, 중단된 코루틴으로 반환해서 실행이 계속 된다.

yield from 사용법은 짧게 다시 정리하면 다음과 같다.

async API에서도 동일한 원칙이 적용되는데,

다음 절에는 앞서 말한 이 질문에 대해서 살펴본다.

둘 다 단일 스레드환경인데, asyncio를 이용하면, 그렇지 않았을 때에 비해서 5배나 빨라졌던 걸까?

블로킹 호출을 에둘러 실행하기

Node.js 창시자 라이언 달(Ryan Dahl)은 ‘우리는 입출력을 완전히 잘못하고 있다’라고 설명하였다. 그는 디스크나 네트워크 입출력을 블로킹 함수로 정의하며 이 함수를 논블로킹 함수처럼 취급하면 안된다고 말한다. 블로킹 함수가 전체 애플리케이션을 멈추게 하지 않기 위해서는 다음과 같은 방법이 있다.

파이썬에서 사용하는 OS 쓰레드는 수 메가 바이트의 메모리를 소비하므로, 수천 개의 연결을 처리하기 위해 윗 방식을 선택하기 어렵다. 전통적으로 콜백으로 비동기 호출을 구현했다. 개념적으로는 하드웨어 인터럽트와 비슷한 저수준 개념이다. 라이언 달은 단순함과 저부하를 이유로 콜백을 지지한다.

asyncio를 이용하면 블로킹되는 부분이 거의 없이 처리할 수 있으나, 순수 버전에서는 이미지를 다운받는동안 블로킹이 되어 성능이 나올 수가 없었던 것이다.