본문 바로가기

Language/Python

Book 파이썬답게 코딩하기 - Asyncio

Asyncio

파이썬답게 코딩하기

파이썬에서 정식으로 제공하는 비동기 논블록 I/O 모듈이다.
Python 3.4에 처음으로 포함되고 버전에 따라 새로운 문법, 기능들이 추가 되었다.
제너레이트 기반 코루틴, yield from을 사용 논블록을 구현했다.

Asyncio in Python 3.4

event loop에 코루틴 작업을 등록해서 사용한다.

asyncio

# generator_base.py
[0] Sleep time : 2, Complete time : 2020-01-07 10:59:56.946342
[5] Sleep time : 2, Complete time : 2020-01-07 10:59:56.946540
[9] Sleep time : 2, Complete time : 2020-01-07 10:59:56.946595
[3] Sleep time : 4, Complete time : 2020-01-07 10:59:58.947012
[8] Sleep time : 4, Complete time : 2020-01-07 10:59:58.947117
[6] Sleep time : 4, Complete time : 2020-01-07 10:59:58.947162
[1] Sleep time : 5, Complete time : 2020-01-07 10:59:59.950178
[7] Sleep time : 6, Complete time : 2020-01-07 11:00:00.948965
[2] Sleep time : 8, Complete time : 2020-01-07 11:00:02.948922
[4] Sleep time : 9, Complete time : 2020-01-07 11:00:03.949042

@asyncio.coroutine 데코레이터로 구현된 코루틴함수 print_time을 비동기 논블록 방식 10번 실행하는 함수다.
코루틴 함수들은 I/O와 관련된 작업이나 CPU를 사용하지 않아도 되는 작업을 처리할 때는 제어권을 양보한다.

Asyncio in Python 3.5

async, await 문법 추가
제너레이터 기반이 아닌 내부적으로 구현한 Python native 코루틴을 사용을 추가했다.
async@asyncio.coroutine을 대체할 수 있고, awaityield from을 대체할 수 있다.

event loop에서 native 코루틴, 제너레이터 코루틴은 상호 운영이 가능하지만 하나의 코루틴에서 2가지 문법을 혼용할 수는 없다.

# native_coroutine.py
# Python 3.4 or later
[1] Sleep time : 1, Complete time : 2020-01-07 11:14:50.021235
[0] Sleep time : 1, Complete time : 2020-01-07 11:14:50.021333
[4] Sleep time : 1, Complete time : 2020-01-07 11:14:50.021356
[5] Sleep time : 2, Complete time : 2020-01-07 11:14:51.026174
[9] Sleep time : 4, Complete time : 2020-01-07 11:14:53.023027
[8] Sleep time : 4, Complete time : 2020-01-07 11:14:53.023143
[3] Sleep time : 7, Complete time : 2020-01-07 11:14:56.022982
[6] Sleep time : 8, Complete time : 2020-01-07 11:14:57.026239
[7] Sleep time : 8, Complete time : 2020-01-07 11:14:57.026345
[2] Sleep time : 8, Complete time : 2020-01-07 11:14:57.026391
# async_for.py
# Python 3.5 or later
The mission of the Python Software Foundation is to promote, protect,
and advance the Python programming language, and to support and
facilitate the growth of a diverse and international community of
Python programmers.

from the Mission Statement page

awaitable

await 구문에서 사용될 수 있는 객체를 말한다.
이터레이를 반환하는 코루틴은 awaitable하다라고 할 수 있다.
이터레이를 반화하는 native 코루틴 이라고 보면 된다.

asynchronous iterable, asynchronous iterator

비동기로 처리될 수 있는 iterableiterator 이다.
async for문은 비동기적으로 처리될 수 있는 asynchronous iterable 대상으로 동작하고,
순회 결과 비동기 처리될 수 있는 asynchronous iterator를 반환한다.

AsynchronousReader 클래스

파일명을 인자로 받아 파일을 열고 iteration를 통해 한 줄씩 읽고 반환하는 클래스다.
file_close 메서드를 통해 닫는다.
async for에서 값을 순회할 때 마다 __anext__ 가 호출되며 이 메서드 반환값이 async for문의 반환값으로 나타난다.

async for문은 asyn로 선언한 함수 native coroutine에서만 사용할 수 있다.

# async_with.py
# Python 3.5 or later
The mission of the Python Software Foundation is to promote, protect,
and advance the Python programming language, and to support and
facilitate the growth of a diverse and international community of
Python programmers.

from the Mission Statement page

Asyncio in Python 3.6

Python 3.5 native coroutine은 제약이 있었다.
yield를 사용하지 못하고 generator base coroutne에서 await구문을 사용하지 못한다.
Python 3.6 에서 이 제약이 사라진다.
native coroutine에서 yield, yield form을 사용할 수 있다.
이로 인해 native coroutine으로 generator를 만들 수 있다.
그리고, asynchronous comprehension을 구현할 수 있도록 기능이 개선 되었다.

크게보 보면 asynchronous generator, asynchronous comprehension 기능이 추가 되었다.

PEP 525 Asynchronous Generators

# async_gernerator.py
# Python 3.6 or later
# PEP 525 Asynchronous Generators: https://www.python.org/dev/peps/pep-0525
0
1
2
3
4
5
6
7
8
9
# async_comprehension.py
# Python 3.6 or later
Async Await Comprehension
Async Comprehension
Async Comprehension
[[0, 1, 2], [0, 1, 2]]

awaityield from역확을 한다.
asynchronous comprehensionnative coroutine 문법만 지원한다.
Pythone 3.6 What's new asyncio를 참고하라.

개념 정리

asyncio를 이해하는데는 event loop, future/task, coroutine이 필요하다.

event loop

비동기 작업을 수행할 때 사용한다.
실행할 작업을 한번에 여러개 등록(schedule)한 뒤 작업을 실행, 지연, 취소할 수 있다.
event loop에서 하나의 작업을 실행했는데 작업 대기 시간이 필요한 경우(I/O 작업) 해당 작업을 일시 중지하고 다른 작업을 먼저 실행한다.
그리고 처음 실행한 작업의 대기 시간이 끝나면 다시 이어서 함수를 실행한다.

future

concurrents.futures와 같은 작업을 관리하는 역할을 한다.
Taskfuture를 래핑한 것이다.
Asyncio에서 작업 관리는 Task 기반으로 객체로 관리한다.
future는 향후 실행될 작업을 의미한다.
그래서 예약(schedule)한다고 표현한다.

코루틴은 async def로 정의되거나 제너레이터 기반으로 만들어진 함수다.
프레임워크와 무관하게 사용할 수 있지만 일반적으로 프레임워크 안에서 구현된 형태로 사용된다.
sayncio에서 event loopcoroutine을 직접 등록, task객체로 만들어서 등록 할 수 있다.
Task객체로 감싸게 될 경우 asyncioevent loop에서 제공하는 메서드를 사용해서 coroutinetask로 변경해서 등록한다.

future(task)coroutine을 혼동할 수 있다.
await구문에 future(task)를 사용하면 future(task)가 완료될 때까지 coroutine을 중단한다.
반면 await구문에 coroutine을 사용하면 다른 coroutine의 반환값을 기다리게 된다.

# coroutine_and_task.py
# Python 3.5 or later
<coroutine object func at 0x105e20a70>
['__await__', '__class__', '__del__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'cr_await', 'cr_code', 'cr_frame', 'cr_origin', 'cr_running', 'send', 'throw']
Start coro
End coro
coro ret : Done
<Task pending coro=<func() running at /python/pythonic/06/coroutine_and_task_dir.py:9>>
['__await__', '__class__', '__del__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_asyncio_future_blocking', '_callbacks', '_coro', '_exception', '_fut_waiter', '_log_destroy_pending', '_log_traceback', '_loop', '_must_cancel', '_repr_info', '_result', '_source_traceback', '_state', 'add_done_callback', 'all_tasks', 'cancel', 'cancelled', 'current_task', 'done', 'exception', 'get_loop', 'get_stack', 'print_stack', 'remove_done_callback', 'result', 'set_exception', 'set_result']
Start task
End task
task ret : Done

coro()에서 출력된 func("coro") 값은 coroutine 객체다.
task() 함수의 결과는 future에서 사용하는 여러가지 메서드가 포홤되어 있어 이 메서드로 동작중 상태 확인 및 취소등을 할 수 있다.
속성 중 _coro 속성은 coro()와 동일한 결과를 가진다.
asyncio.ensure_future를 이용 task로 변환해주는 역활을 하는데 래핑시 넘겨진 coroutine을 따로 보관한 것이다.

AbstratEventLoop.create_task() vs Asyncio.ensure_future()

AbstratEventLoop.create_task(), Asyncio.ensure_future() 둘다 인자로 받은 coroutinefuture로 래핑해서 task로 반환한다.
둘다 같은 기능이지만 용도와 기능이 다르다.
asyncio의 github 프로젝트에 이름 변경과 관련된 제안이 올라 왓고 여기서 토론이 열리게 되었다.
이 토론에서 귀로 반 로섬이 코멘트로 각각의 용도를 설명하기도 했다.

AbstractEventLoop.create_task()Python 3.4에서 asyncio가 파이썬에 포함될 때 함계 추가 됐다.
AbstractEventLoop.create_task()coroutine(코루틴)을 객체를 만들어 주는 메스드다.
ensure_future()와 동일한 역할을 하지만,
중요한 점은 AbstractEventLoop.create_task()AbstractEventLoop안에 전의된 메드드라는 것이다.
바로 event_loop에서 작업을 등록하기 위해 제공하는 메서드다.

비동기 로직을 구현할 때 보통 event loop를 사용한다.
자체적으로 event loop를 구현할 때 이 AbstractEventLoop를 상속받아서 구현하게 한다.
그리고, 구현한 event loop에 작업을 등록할 때 create_task를 사용하게 된다.
즉, event loop특성에 맞는 작업을 생성하기 위해 AbstractEventLoop.create_task()를 사용한다.

반면 asyncio.ensure_func()asyncio 모듈에 정의되어 있는 메서드다.
asyncio.asyncio라는 이름이 Python 3.5버전에서 asyncio.ensure_func()로 변경됐다.
즉, asyncio 모듈에서 사용하는 task를 만들고 이 task의 실행을 등록(예약)하기 위한 메서드다.

asyncio 모듈에 있는 event loop를 사용하다면 asyncio.ensure_func()를 사용해서 작업 관리를 하면 된다.
별도 커스트마이징한 event loop를 사용하면 event loop에서 제공하는 create_task를 사용하면 된다.

기능차이는 AbstractEventLoop.create_task()coroutine을 받는다.
asyncio.ensure_func()coroutine, future object, task object를 입력 받을 수 있다.
coroutine을 받으면 future로 래핑한 task를 반환하고, future, task 객체를 받으면 그대로 반환한다.
Python 3.5에서 awaitable한 객체도 받을 수 있도록 기능이 추가됐다.

Python 3.7에서 asyncio.create_task객체가 추가 됐다.
AbstractEventLoop.create_task()와 유사하고 task를 반환하지만, 작업을 등록(예약)하지는 않고, 순수하게 task를 만들기만 한다.

# native_couroutine_with_task.py
# Python 3.5 or later
[0] task is cancelled
[2] task is cancelled
[4] task is cancelled
[6] task is cancelled
[8] task is cancelled
[1] Sleep time : 1, Complete time : 2020-01-08 11:57:27.732474
[1] Call callback function
[5] Sleep time : 2, Complete time : 2020-01-08 11:57:28.734988
[5] Call callback function
[7] Sleep time : 3, Complete time : 2020-01-08 11:57:29.734890
[9] Sleep time : 3, Complete time : 2020-01-08 11:57:29.735020
[7] Call callback function
[9] Call callback function
[3] Sleep time : 4, Complete time : 2020-01-08 11:57:30.735668
[3] Call callback function