SlideShare a Scribd company logo
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
async/await の向こう側
PyCon Kyushu 2022 Kumamoto
Atsushi Odagiri
2022-01-22
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
はじめに
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
お前誰よ
aodag
Atsushi Odagiri
株式会社オープンコレクター
python1.5 とかから
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
アジェンダ
asyncio とコルーチン
コルーチン
非同期 IO
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
async/await と asyncio
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
async/await と asyncio
Python 3.4 asyncio 導入
Python 3.5 async/await 構文導入
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
asyncio
非同期 IO をコルーチンで扱えるライブラリ
コルーチンを扱うための関数も用意されている
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
async/await
コルーチンを扱うための構文
await でサブコルーチンを呼び出して大きなコルーチンを作る
await にはサブコルーチンが必要
await だけでコルーチンを作ることができない
__await__ スペシャルメソッドで awaitable な型を定義
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
コルーチンとは?
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
並列処理
並列処理 (concurrent)
プリエンプティブなスレッド
コルーチン (協調スレッド)
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
プリエンプティブな並列処理
主に OS の機能を使う
gevent のようにユーザーランドのみで行う実装もある
コンテキストの切り替えは実行中のプログラムが意識するこ
となく行われる
いつ切り替わるかわからないので共有リソースの扱いに注意
IO 待ちにる場合なども自動で行われるように作られている
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
協調スレッドによる並列処理
協調スレッド
自分で処理を明け渡す
yield を使う
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
どうしてジェネレーターでコルーチンが作れるのか?
yield で処理を明け渡す
send されると値を受け取って処理を再開する
yield from でコルーチンの結果を取得
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
ジェネレーターでコルーチンを書く
def counter(n: int, name: str) -> typing.Generator[None, No
count = 0
while count < n:
yield
print(f"{name}: {count}")
count += 1
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
ジェネレーターで書いたコルーチンを動かす
coroutines = [counter(10, "counter1"), counter(5, "counter2
while coroutines:
for c in coroutines:
try:
c.send(None)
except StopIteration:
coroutines.remove(c)
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
コルーチンを動かしてみた結果
counter1: 0
counter2: 0
counter1: 1
counter2: 1
counter1: 2
counter2: 2
counter1: 3
counter2: 3
counter1: 4
counter2: 4
counter1: 5
counter1: 6
counter1: 7
counter1: 8
counter1: 9
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
yield from で結果を取得
yield from でサブコルーチンを呼ぶ
サブコルーチンまで含めて大きなコルーチンになる
サブコルーチンが return した値が yield from で返って
くる
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
サブコルーチンを yield from で呼ぶ
def twice_counter() -> typing.Generator[None, None, None]:
yield from counter(10, "counter1")
yield from counter(5, "counter2")
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
yield 式って結局なに?
next したときにジェネレーターとしての要素を返す
次に next されるまで処理が止まる
値を渡すときは send を使う
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
await vs yield from
await サブコルーチンを起動して、結果を受け取る
yield from サブジェネレータを起動して、ジェネレータの
要素とする + 結果を受け取る
await は途中経過を気にしないようになって、よりコルーチ
ンとしての機能に特化している
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
タスク!future!コルーチンを利用するための
ツール
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
イベントループ
イベントについては後述
コルーチンはジェネレーターなので send してくれるループ
が必要
複数のコルーチンを順番に少しずつ send することでそれぞ
れのコルーチンが実行される
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
イベントループの素朴な実装
非同期 IO に関する処理
tasks: typing.List[typing.Generator[None, None, None]] = []
removing: typing.Set[typing.Generator[None, None, None]] =
while True:
for t in tasks:
try:
t.send(None)
except StopIteration:
removing.add(t)
except Exception as e:
print(e)
removing.add(t)
for t in removing:
tasks.remove(t)
removing = set()
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
future で非同期に結果を得る
結果が入る場所を用意してコルーチンに渡す
他のコルーチンで後から結果を確認
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
Future の素朴な実装
class Future(typing.Generic[T]):
_result: typing.Union[T, None]
def __init__(self):
self._result = None
self._done = False
def done(self, value: T):
self._result = value
self._done = True
def get_result(self) -> typing.Generator[None, None, T]
while self._result is None:
yield
return self._result
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
コルーチンと future をまとめてタスクにする
タスクは受け取ったコルーチンをサブコルーチン呼び出して
結果を future に入れるコルーチン
並列で実行するようにタスクをイベントループに追加
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
タスクの素朴な実装
def task(coro: typing.Generator[None, None, T], f: Future[T
result: T = yield from coro
f.done(result)
def create_task(coro: typing.Generator[None, None, T]) -> t
f: Future[T] = Future()
tasks.append(task(coro, f))
return f.get_result()
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
タスクを使った非同期処理
def twice_counter() -> typing.Generator[None, None, None]:
t1 = create_task(counter(10, "counter1"))
t2 = create_task(counter(5, "counter2"))
yield from t1
yield from t2
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
非同期 IO
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
非同期 IO
非同期 IO とはなにか
IO 待ちしている間は CPU は暇
IO 待ちしている間に別のタスクを実行したい
IO の状況が変わるまで放置
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
イベントループのイベントとは?
IO が読み取り可能になった
IO が書き込み可能になった
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
非同期 IO(コールバック)
コールバックで IO を待つ
イベント発生時にコールバックを呼ぶ
コールバックが呼ばれるまで他の処理ができる
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
selectors モジュール
非同期 IO
select/poll などの標準的な仕組み
kequeue, epoll, IOCP のような OS ごとの仕組み
selectors モジュールは様々な OS 向けの非同期 IO の仕組みを
共通のインターフェイスで使えるようにしたラッパー
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
selectors モジュールを使う処理
監視対象を登録する register
ポーリングしてイベントを取得 select
監視を解除 unregister
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
非同期 IO をコルーチンで処理する
イベントループ内でポーリングする
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
素朴な echo サーバー
accept したらそのまま echo 処理を行う
1 つのクライアントの処理が終わらないと他のクライアント
の接続を処理できない
sock = socket.socket()
sock.bind(('localhost', 1234))
sock.listen(100)
while True:
conn, addr = sock.accept()
while True:
data = conn.recv(1000)
if data:
conn.send(data)
else:
conn.close()
break
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
マルチスレッドな echo サーバー
accept したら子スレッドを作成してクライアントソケットを
渡す
def accept(conn: socket.socket):
try:
data = conn.recv(1000)
while data:
conn.send(data)
data = conn.recv(1000)
finally:
conn.close()
while True:
conn, _ = sock.accept()
threading.Thread(target=accept, args=(conn,)).start()
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
スレッドプールを使う echo サーバー
accept したらクライアントソケットをスレッドプールに渡す
executor = ThreadPoolExecutor()
while True:
conn, _ = sock.accept()
executor.submit(accept, conn)
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
selector にコールバックを使って echo サーバー
コールバック処理
def accept(sock, _):
conn, addr = sock.accept() # Should be ready
print('accepted', conn, 'from', addr)
conn.setblocking(False)
sel.register(conn, selectors.EVENT_READ, read)
def read(conn, _):
data = conn.recv(1000) # Should be ready
if data:
print('echoing', repr(data), 'to', conn)
conn.send(data) # Hope it won't block
else:
print('closing', conn)
sel.unregister(conn)
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
selector にコールバックを使って echo サーバー
selector や socket の準備
sel = selectors.DefaultSelector()
sock = socket.socket()
sock.bind(('localhost', 1234))
sock.listen(100)
sock.setblocking(False)
sel.register(sock, selectors.EVENT_READ, accept)
while True:
events = sel.select()
for key, mask in events:
callback = key.data
callback(key.fileobj, mask)
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
selector をコルーチンで呼び出せるようにして echo サー
バー (1)
recv をコルーチンで扱えるようにする
def read_async(conn:socket.socket):
f: Future[bytes] = Future()
def callback(conn: socket.socket, _):
sel.unregister(conn)
f.done(conn.recv(1000))
sel.register(conn, selectors.EVENT_READ, callback)
return f.get_result()
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
selector をコルーチンで呼び出せるようにして echo サー
バー (2)
send をコルーチンで扱えるようにする
def send_async(conn:socket.socket, data: bytes):
f: Future[int] = Future()
def callback(conn: socket.socket, _):
sel.unregister(conn)
f.done(conn.send(data))
sel.register(conn, selectors.EVENT_WRITE, callback)
return f.get_result()
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
selector をコルーチンで呼び出せるようにして echo サー
バー (3)
echo タスク
def accept(sock, _):
conn, _ = sock.accept()
conn.setblocking(False)
create_task(read(conn))
def read(conn: socket.socket):
while True:
data = yield from read_async(conn)
if data:
yield from send_async(conn, data)
else:
break
conn.close()
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
selector をコルーチンで呼び出せるようにして echo サー
バー (4)
selector やソケットの準備
sel = selectors.DefaultSelector()
tasks: typing.List[typing.Generator[None, None, None]] = []
sock = socket.socket()
sock.bind(('localhost', 1234))
sock.listen(100)
sock.setblocking(False)
sel.register(sock, selectors.EVENT_READ, accept)
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
selector をコルーチンで呼び出せるようにして echo サー
バー (5)
イベントループ
while True:
events = sel.select()
for key, mask in events:
callback = key.data
callback(key.fileobj, mask)
removing: typing.Set[typing.Generator[None, None, None]
for t in tasks:
try:
t.send(None)
except StopIteration:
removing.add(t)
except Exception as e:
print(e)
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
おまけ
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
コルーチンでない awaitable
class AwaitableSample:
def __await__(self):
yield
return "hello"
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
コルーチンでない!
タスクが受け取るのはコルーチンであって awaitable 全般
ではないらしい
asyncio.create_task(AwaitableSample())
TypeError: a coroutine was expected,
got <__main__.AwaitableSample object at 0x7f4429c36100>
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
まとめ
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
まとめ
コルーチン
yield で処理を明け渡す協調スレッド (コルーチン)
yield from はコルーチンを起動して終了を待機する
タスクは起動したコルーチンと結果を受け取る Future の組
非同期 IO
OS ごとに異なる方法で IO の状態を監視
ポーリングして状態チェック
クライアント接続ごとにスレッドを消費しない
状態が変化するまでコルーチンで待つ
Atsushi Odagiri
async/await の向こう側
はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お
参考文献
PEP 255 – Simple Generators
PEP 380 – Syntax for Delegating to a Subgenerator -PEP 492
– Coroutines with async and await syntax
PEP 3156 – Asynchronous IO Support Rebooted: the “asyncio”
Module
Python3.4 当時の asyncio リファレンス
Atsushi Odagiri
async/await の向こう側

More Related Content

PDF
ジェネリック関数の呼び出され方 #cocoa_kansai
PDF
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
PDF
Swift 2.0 で変わったところ「後編」 #cswift
PDF
Swift 2.0 の Error Handling #yhios
PDF
Pythonによる非同期プログラミング入門
PDF
20分くらいでわかった気分になれるC++20コルーチン
PDF
Pythonの非同期処理を始める前に
PPTX
モダン JavaScript における非同期処理 - Promise, async/await -
ジェネリック関数の呼び出され方 #cocoa_kansai
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
Swift 2.0 で変わったところ「後編」 #cswift
Swift 2.0 の Error Handling #yhios
Pythonによる非同期プログラミング入門
20分くらいでわかった気分になれるC++20コルーチン
Pythonの非同期処理を始める前に
モダン JavaScript における非同期処理 - Promise, async/await -

Similar to async/await の向こう側 PyCon Kyushu 2022 (20)

PDF
コルーチンの使い方
PPTX
大規模分散システムの現在 -- Twitter
PPTX
SpringIO2019報告_Kotlin関連
PDF
Introduction Pycon2010
PPTX
async / await の話
PDF
できる!並列・並行プログラミング
PPTX
Too difficult concurrent processing
PDF
Promise async await
PDF
Async Enhancement
PDF
async/await不要論
PDF
これからの「async/await」の話をしよう
PDF
LLdeade Python Language Update
PDF
Pyconjp2014_implementations
PDF
PyPy 紹介
PDF
パターンでわかる! .NET Coreの非同期処理
PDF
いまさら恥ずかしくてAsyncをawaitした
PDF
非同期の時代がやってくる!
PDF
Python & PyConJP 2014 Report
PDF
次世代言語 Python による PyPy を使った次世代の処理系開発
PDF
Async flow controll basic and practice
コルーチンの使い方
大規模分散システムの現在 -- Twitter
SpringIO2019報告_Kotlin関連
Introduction Pycon2010
async / await の話
できる!並列・並行プログラミング
Too difficult concurrent processing
Promise async await
Async Enhancement
async/await不要論
これからの「async/await」の話をしよう
LLdeade Python Language Update
Pyconjp2014_implementations
PyPy 紹介
パターンでわかる! .NET Coreの非同期処理
いまさら恥ずかしくてAsyncをawaitした
非同期の時代がやってくる!
Python & PyConJP 2014 Report
次世代言語 Python による PyPy を使った次世代の処理系開発
Async flow controll basic and practice
Ad

More from Atsushi Odagiri (20)

PDF
みんなのPython勉強会#77 パッケージングしよう
PDF
パッケージングの呼び声 Python Charity Talks in Japan 2021.02
PDF
eggとはなんだったのか 栄光のsetuptools
PDF
pyconjp 2019 LT 今日のsetuptools
PDF
Pythonでの開発を効率的に進めるためのツール設定
PDF
Pythonとパッケージングと私
PDF
Python3 移行への軌跡
PDF
パッケージングを支える技術 pyconjp2016
PDF
Sqlalchemy sqlの錬金術
PPTX
Clack meetup #1 lt
PDF
Pyramid入門
PDF
パッケージングの今
PDF
パッケージングの今と未来
PDF
パッケージングの今と未来
PDF
Bplt11 form alchemy
PDF
Python3でwebアプリ
PDF
Pyconjp2012 memory-of-europython
PDF
What makes pyramid unique
PDF
エキPy lt repoze.whoの紹介
PDF
World plonedaylt
みんなのPython勉強会#77 パッケージングしよう
パッケージングの呼び声 Python Charity Talks in Japan 2021.02
eggとはなんだったのか 栄光のsetuptools
pyconjp 2019 LT 今日のsetuptools
Pythonでの開発を効率的に進めるためのツール設定
Pythonとパッケージングと私
Python3 移行への軌跡
パッケージングを支える技術 pyconjp2016
Sqlalchemy sqlの錬金術
Clack meetup #1 lt
Pyramid入門
パッケージングの今
パッケージングの今と未来
パッケージングの今と未来
Bplt11 form alchemy
Python3でwebアプリ
Pyconjp2012 memory-of-europython
What makes pyramid unique
エキPy lt repoze.whoの紹介
World plonedaylt
Ad

async/await の向こう側 PyCon Kyushu 2022

  • 1. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お async/await の向こう側 PyCon Kyushu 2022 Kumamoto Atsushi Odagiri 2022-01-22 Atsushi Odagiri async/await の向こう側
  • 2. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お はじめに Atsushi Odagiri async/await の向こう側
  • 3. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お お前誰よ aodag Atsushi Odagiri 株式会社オープンコレクター python1.5 とかから Atsushi Odagiri async/await の向こう側
  • 4. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お アジェンダ asyncio とコルーチン コルーチン 非同期 IO Atsushi Odagiri async/await の向こう側
  • 5. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お async/await と asyncio Atsushi Odagiri async/await の向こう側
  • 6. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お async/await と asyncio Python 3.4 asyncio 導入 Python 3.5 async/await 構文導入 Atsushi Odagiri async/await の向こう側
  • 7. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お asyncio 非同期 IO をコルーチンで扱えるライブラリ コルーチンを扱うための関数も用意されている Atsushi Odagiri async/await の向こう側
  • 8. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お async/await コルーチンを扱うための構文 await でサブコルーチンを呼び出して大きなコルーチンを作る await にはサブコルーチンが必要 await だけでコルーチンを作ることができない __await__ スペシャルメソッドで awaitable な型を定義 Atsushi Odagiri async/await の向こう側
  • 9. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お コルーチンとは? Atsushi Odagiri async/await の向こう側
  • 10. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お 並列処理 並列処理 (concurrent) プリエンプティブなスレッド コルーチン (協調スレッド) Atsushi Odagiri async/await の向こう側
  • 11. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お プリエンプティブな並列処理 主に OS の機能を使う gevent のようにユーザーランドのみで行う実装もある コンテキストの切り替えは実行中のプログラムが意識するこ となく行われる いつ切り替わるかわからないので共有リソースの扱いに注意 IO 待ちにる場合なども自動で行われるように作られている Atsushi Odagiri async/await の向こう側
  • 12. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お 協調スレッドによる並列処理 協調スレッド 自分で処理を明け渡す yield を使う Atsushi Odagiri async/await の向こう側
  • 13. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お どうしてジェネレーターでコルーチンが作れるのか? yield で処理を明け渡す send されると値を受け取って処理を再開する yield from でコルーチンの結果を取得 Atsushi Odagiri async/await の向こう側
  • 14. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お ジェネレーターでコルーチンを書く def counter(n: int, name: str) -> typing.Generator[None, No count = 0 while count < n: yield print(f"{name}: {count}") count += 1 Atsushi Odagiri async/await の向こう側
  • 15. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お ジェネレーターで書いたコルーチンを動かす coroutines = [counter(10, "counter1"), counter(5, "counter2 while coroutines: for c in coroutines: try: c.send(None) except StopIteration: coroutines.remove(c) Atsushi Odagiri async/await の向こう側
  • 16. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お コルーチンを動かしてみた結果 counter1: 0 counter2: 0 counter1: 1 counter2: 1 counter1: 2 counter2: 2 counter1: 3 counter2: 3 counter1: 4 counter2: 4 counter1: 5 counter1: 6 counter1: 7 counter1: 8 counter1: 9 Atsushi Odagiri async/await の向こう側
  • 17. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お yield from で結果を取得 yield from でサブコルーチンを呼ぶ サブコルーチンまで含めて大きなコルーチンになる サブコルーチンが return した値が yield from で返って くる Atsushi Odagiri async/await の向こう側
  • 18. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お サブコルーチンを yield from で呼ぶ def twice_counter() -> typing.Generator[None, None, None]: yield from counter(10, "counter1") yield from counter(5, "counter2") Atsushi Odagiri async/await の向こう側
  • 19. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お yield 式って結局なに? next したときにジェネレーターとしての要素を返す 次に next されるまで処理が止まる 値を渡すときは send を使う Atsushi Odagiri async/await の向こう側
  • 20. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お await vs yield from await サブコルーチンを起動して、結果を受け取る yield from サブジェネレータを起動して、ジェネレータの 要素とする + 結果を受け取る await は途中経過を気にしないようになって、よりコルーチ ンとしての機能に特化している Atsushi Odagiri async/await の向こう側
  • 21. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お タスク!future!コルーチンを利用するための ツール Atsushi Odagiri async/await の向こう側
  • 22. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お イベントループ イベントについては後述 コルーチンはジェネレーターなので send してくれるループ が必要 複数のコルーチンを順番に少しずつ send することでそれぞ れのコルーチンが実行される Atsushi Odagiri async/await の向こう側
  • 23. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お イベントループの素朴な実装 非同期 IO に関する処理 tasks: typing.List[typing.Generator[None, None, None]] = [] removing: typing.Set[typing.Generator[None, None, None]] = while True: for t in tasks: try: t.send(None) except StopIteration: removing.add(t) except Exception as e: print(e) removing.add(t) for t in removing: tasks.remove(t) removing = set() Atsushi Odagiri async/await の向こう側
  • 24. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お future で非同期に結果を得る 結果が入る場所を用意してコルーチンに渡す 他のコルーチンで後から結果を確認 Atsushi Odagiri async/await の向こう側
  • 25. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お Future の素朴な実装 class Future(typing.Generic[T]): _result: typing.Union[T, None] def __init__(self): self._result = None self._done = False def done(self, value: T): self._result = value self._done = True def get_result(self) -> typing.Generator[None, None, T] while self._result is None: yield return self._result Atsushi Odagiri async/await の向こう側
  • 26. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お コルーチンと future をまとめてタスクにする タスクは受け取ったコルーチンをサブコルーチン呼び出して 結果を future に入れるコルーチン 並列で実行するようにタスクをイベントループに追加 Atsushi Odagiri async/await の向こう側
  • 27. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お タスクの素朴な実装 def task(coro: typing.Generator[None, None, T], f: Future[T result: T = yield from coro f.done(result) def create_task(coro: typing.Generator[None, None, T]) -> t f: Future[T] = Future() tasks.append(task(coro, f)) return f.get_result() Atsushi Odagiri async/await の向こう側
  • 28. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お タスクを使った非同期処理 def twice_counter() -> typing.Generator[None, None, None]: t1 = create_task(counter(10, "counter1")) t2 = create_task(counter(5, "counter2")) yield from t1 yield from t2 Atsushi Odagiri async/await の向こう側
  • 29. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お 非同期 IO Atsushi Odagiri async/await の向こう側
  • 30. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お 非同期 IO 非同期 IO とはなにか IO 待ちしている間は CPU は暇 IO 待ちしている間に別のタスクを実行したい IO の状況が変わるまで放置 Atsushi Odagiri async/await の向こう側
  • 31. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お イベントループのイベントとは? IO が読み取り可能になった IO が書き込み可能になった Atsushi Odagiri async/await の向こう側
  • 32. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お 非同期 IO(コールバック) コールバックで IO を待つ イベント発生時にコールバックを呼ぶ コールバックが呼ばれるまで他の処理ができる Atsushi Odagiri async/await の向こう側
  • 33. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お selectors モジュール 非同期 IO select/poll などの標準的な仕組み kequeue, epoll, IOCP のような OS ごとの仕組み selectors モジュールは様々な OS 向けの非同期 IO の仕組みを 共通のインターフェイスで使えるようにしたラッパー Atsushi Odagiri async/await の向こう側
  • 34. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お selectors モジュールを使う処理 監視対象を登録する register ポーリングしてイベントを取得 select 監視を解除 unregister Atsushi Odagiri async/await の向こう側
  • 35. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お 非同期 IO をコルーチンで処理する イベントループ内でポーリングする Atsushi Odagiri async/await の向こう側
  • 36. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お 素朴な echo サーバー accept したらそのまま echo 処理を行う 1 つのクライアントの処理が終わらないと他のクライアント の接続を処理できない sock = socket.socket() sock.bind(('localhost', 1234)) sock.listen(100) while True: conn, addr = sock.accept() while True: data = conn.recv(1000) if data: conn.send(data) else: conn.close() break Atsushi Odagiri async/await の向こう側
  • 37. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お マルチスレッドな echo サーバー accept したら子スレッドを作成してクライアントソケットを 渡す def accept(conn: socket.socket): try: data = conn.recv(1000) while data: conn.send(data) data = conn.recv(1000) finally: conn.close() while True: conn, _ = sock.accept() threading.Thread(target=accept, args=(conn,)).start() Atsushi Odagiri async/await の向こう側
  • 38. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お スレッドプールを使う echo サーバー accept したらクライアントソケットをスレッドプールに渡す executor = ThreadPoolExecutor() while True: conn, _ = sock.accept() executor.submit(accept, conn) Atsushi Odagiri async/await の向こう側
  • 39. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お selector にコールバックを使って echo サーバー コールバック処理 def accept(sock, _): conn, addr = sock.accept() # Should be ready print('accepted', conn, 'from', addr) conn.setblocking(False) sel.register(conn, selectors.EVENT_READ, read) def read(conn, _): data = conn.recv(1000) # Should be ready if data: print('echoing', repr(data), 'to', conn) conn.send(data) # Hope it won't block else: print('closing', conn) sel.unregister(conn) Atsushi Odagiri async/await の向こう側
  • 40. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お selector にコールバックを使って echo サーバー selector や socket の準備 sel = selectors.DefaultSelector() sock = socket.socket() sock.bind(('localhost', 1234)) sock.listen(100) sock.setblocking(False) sel.register(sock, selectors.EVENT_READ, accept) while True: events = sel.select() for key, mask in events: callback = key.data callback(key.fileobj, mask) Atsushi Odagiri async/await の向こう側
  • 41. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お selector をコルーチンで呼び出せるようにして echo サー バー (1) recv をコルーチンで扱えるようにする def read_async(conn:socket.socket): f: Future[bytes] = Future() def callback(conn: socket.socket, _): sel.unregister(conn) f.done(conn.recv(1000)) sel.register(conn, selectors.EVENT_READ, callback) return f.get_result() Atsushi Odagiri async/await の向こう側
  • 42. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お selector をコルーチンで呼び出せるようにして echo サー バー (2) send をコルーチンで扱えるようにする def send_async(conn:socket.socket, data: bytes): f: Future[int] = Future() def callback(conn: socket.socket, _): sel.unregister(conn) f.done(conn.send(data)) sel.register(conn, selectors.EVENT_WRITE, callback) return f.get_result() Atsushi Odagiri async/await の向こう側
  • 43. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お selector をコルーチンで呼び出せるようにして echo サー バー (3) echo タスク def accept(sock, _): conn, _ = sock.accept() conn.setblocking(False) create_task(read(conn)) def read(conn: socket.socket): while True: data = yield from read_async(conn) if data: yield from send_async(conn, data) else: break conn.close() Atsushi Odagiri async/await の向こう側
  • 44. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お selector をコルーチンで呼び出せるようにして echo サー バー (4) selector やソケットの準備 sel = selectors.DefaultSelector() tasks: typing.List[typing.Generator[None, None, None]] = [] sock = socket.socket() sock.bind(('localhost', 1234)) sock.listen(100) sock.setblocking(False) sel.register(sock, selectors.EVENT_READ, accept) Atsushi Odagiri async/await の向こう側
  • 45. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お selector をコルーチンで呼び出せるようにして echo サー バー (5) イベントループ while True: events = sel.select() for key, mask in events: callback = key.data callback(key.fileobj, mask) removing: typing.Set[typing.Generator[None, None, None] for t in tasks: try: t.send(None) except StopIteration: removing.add(t) except Exception as e: print(e) Atsushi Odagiri async/await の向こう側
  • 46. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お おまけ Atsushi Odagiri async/await の向こう側
  • 47. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お コルーチンでない awaitable class AwaitableSample: def __await__(self): yield return "hello" Atsushi Odagiri async/await の向こう側
  • 48. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お コルーチンでない! タスクが受け取るのはコルーチンであって awaitable 全般 ではないらしい asyncio.create_task(AwaitableSample()) TypeError: a coroutine was expected, got <__main__.AwaitableSample object at 0x7f4429c36100> Atsushi Odagiri async/await の向こう側
  • 49. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お まとめ Atsushi Odagiri async/await の向こう側
  • 50. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お まとめ コルーチン yield で処理を明け渡す協調スレッド (コルーチン) yield from はコルーチンを起動して終了を待機する タスクは起動したコルーチンと結果を受け取る Future の組 非同期 IO OS ごとに異なる方法で IO の状態を監視 ポーリングして状態チェック クライアント接続ごとにスレッドを消費しない 状態が変化するまでコルーチンで待つ Atsushi Odagiri async/await の向こう側
  • 51. はじめに async/await と asyncio コルーチンとは? タスク!future!コルーチンを利用するためのツール 非同期 IO お 参考文献 PEP 255 – Simple Generators PEP 380 – Syntax for Delegating to a Subgenerator -PEP 492 – Coroutines with async and await syntax PEP 3156 – Asynchronous IO Support Rebooted: the “asyncio” Module Python3.4 当時の asyncio リファレンス Atsushi Odagiri async/await の向こう側