LoginSignup
2
0

More than 1 year has passed since last update.

trioによる並行処理④(Nursery)

Last updated at Posted at 2022-09-29

目次

trioNurseryをもう少し深掘りします。
余談ですが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
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0