trioのNurseryをもう少し深掘りします。
余談ですがNurseryは保育園とか苗床という意味のようです(参考)。
保育園で子供(子タスク)をどんどん走らせるイメージでいると中々可愛いですね。
Nursery.start_soon()
trioによる並行処理①の記事で紹介したように、start_soon()メソッドを使用することで子タスクを実行できます。
import trio
async def add(a, b):
print("add start")
await trio.sleep(1)
print(f"add done: {a} + {b} = {a + b}")
async def sub(a, b):
print("sub start")
await trio.sleep(1)
print(f"sub done: {a} - {b} = {a - b}")
async def main():
async with trio.open_nursery() as nursery:
nursery.start_soon(add, 3, 2)
nursery.start_soon(sub, 1, 3)
print("end of nursery")
print("all done")
trio.run(main)
実行結果例
end of nursery
add start
sub start
add done: 3 + 2 = 5
sub done: 1 - 3 = -2
all done
start_soon()
メソッドを使った場合、子タスクの開始を待たずにその下の処理が実行されます。
そのためend of nursery
が最初に出力されます。
尚、start_soon
はデコレータとして使うこともできます(関数の上に@nursery.start_soon
を付ける)。
例えば下記は先ほどの結果と同じになります。
import trio
async def main():
async with trio.open_nursery() as nursery:
a = 3
b = 2
@nursery.start_soon
async def add():
print("add start")
await trio.sleep(1)
print(f"add done: {a} + {b} = {a + b}")
c = 1
d = 3
@nursery.start_soon
async def sub():
print("sub start")
await trio.sleep(1)
print(f"sub done: {c} - {d} = {c - d}")
print("end of nursery")
print("all done")
trio.run(main)
Nursery.cancel_scope.cancel()
start_soon()
と同様に子タスクを走らせるstart()メソッドがあります。
-
start_soon()
は同期関数ですが、start()
は非同期関数です。
そのためawait start(非同期関数)
のように呼ぶ必要があります。 -
start()
に渡す非同期関数は必ずtask_status
というキーワードの引数が必要であり、関数内でtask_status.started()
を呼ぶ必要があります。その非同期関数は次のように定義します。ここでasync def 非同期関数(引数1, 引数2, *, task_status=trio.TASK_STATUS_IGNORED): ... task_status.started() ...
trio.TASK_STATUS_IGNORED
はグローバル変数であり、上記の通りstarted()
というメソッドを持ちます。 -
start_soon()
と違いstart()
を使った場合はその下の処理がすぐに実行されないです。
しかし、task_status.started()
が呼ばれるとstart()
の下の処理が実行されます。
サンプル
import trio
async def add(a, b, *, task_status=trio.TASK_STATUS_IGNORED):
print("add start")
await trio.sleep(1)
print("task_status.started() start")
task_status.started()
print("task_status.started() done")
await trio.sleep(1)
print(f"add done: {a} + {b} = {a + b}")
async def sub(a, b):
print("sub start")
await trio.sleep(2)
print(f"sub done: {a} - {b} = {a - b}")
async def main():
async with trio.open_nursery() as nursery:
await nursery.start(add, 3, 2)
nursery.start_soon(sub, 1, 3)
print("end of nursery")
print("all done")
trio.run(main)
実行結果例
add start
task_status.started() start
task_status.started() done
end of nursery
sub start
sub done: 1 - 3 = -2
add done: 3 + 2 = 5
all done
このように
-
start()
の下の処理がすぐには実行されないため、"end of nursery"よりも先に"add start"が出力されます -
task_status.started()
が呼ばれたタイミングでstart()
の下の処理が進み、"end of nursery"が出力されます- 厳密には非同期な
await trio.sleep(1)
が呼ばれたタイミングでstart()
の下の処理が進むと思われます(そのため、"end of nursery"よりも先に"task_status.started() done"が出力されています)
- 厳密には非同期な
尚、task_status.started()
には値を渡すことができ、それがstart()
の返り値となります。
サンプル
import trio
async def add(a, b, *, task_status=trio.TASK_STATUS_IGNORED):
print("add start")
await trio.sleep(1)
result = a + b
task_status.started(result)
print(f"add done: {a} + {b} = {result}")
async def main():
async with trio.open_nursery() as nursery:
result = await nursery.start(add, 3, 2)
print(f"end of nursery (result={result})")
print("all done")
trio.run(main)
実行結果
add start
add done: 3 + 2 = 5
end of nursery (result=5)
all done
Nursery.start_soon()
trioによる並行処理①の記事で紹介したように、start_soon()メソッドを使用することで子タスクを実行できます。
import trio
async def add(a, b):
print("add start")
await trio.sleep(1)
print(f"add done: {a} + {b} = {a + b}")
async def sub(a, b):
print("sub start")
await trio.sleep(1)
print(f"sub done: {a} - {b} = {a - b}")
async def main():
async with trio.open_nursery() as nursery:
nursery.start_soon(add, 3, 2)
nursery.start_soon(sub, 1, 3)
print("end of nursery")
print("all done")
trio.run(main)
実行結果例
end of nursery
add start
sub start
add done: 3 + 2 = 5
sub done: 1 - 3 = -2
all done