web-dev-qa-db-ja.com

実行中の非同期ループにコルーチンを追加する方法は?

実行中のasyncioループに新しいコルーチンを追加するにはどうすればよいですか?すなわち。すでにコルーチンのセットを実行しているもの。

回避策として、既存のコルーチンが完了するのを待ってから、新しいループを(追加のコルーチンを使用して)初期化できると思います。しかし、もっと良い方法はありますか?

24
Petri

create_task 新しいコルーチンをスケジュールする場合:

import asyncio

async def cor1():
    ...

async def cor2():
    ...

async def main(loop):
    await asyncio.sleep(0)
    t1 = loop.create_task(cor1())
    await cor2()
    await t1

loop = asyncio.get_event_loop()
loop.run_until_complete(main(loop))
loop.close()
19
Jashandeep Sohi

既に実行中のイベントループに関数を追加するには、次を使用できます。

asyncio.ensure_future(my_coro())

私の場合、マルチスレッド(threading)をasyncioとともに使用しており、既に実行中のイベントループにタスクを追加したいと考えていました。同じ状況にいる他の人には、イベントループを明示的に記述してください(Thread内には存在しないため)。すなわち:

グローバルスコープ:

event_loop = asyncio.get_event_loop()

その後、Thread内で:

asyncio.ensure_future(my_coro(), loop=event_loop)
13
Dotl

あなたの質問は「実行中のプログラムに関数呼び出しを追加する方法」に非常に近いです。

イベントループに新しいコルーチンを追加する必要があるのはいつですか?

いくつかの例を見てみましょう。ここで、2つのコルーチンを並行して使用してイベントループを開始するプログラム:

import asyncio
from random import randint


async def coro1():
    res = randint(0,3)
    await asyncio.sleep(res)
    print('coro1 finished with output {}'.format(res))
    return res

async def main():
    await asyncio.gather(
        coro1(),
        coro1()
    ) # here we have two coroutines running parallely

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

出力:

coro1 finished with output 1
coro1 finished with output 2
[Finished in 2.2s]

coro1の結果を取得し、準備ができたらすぐに使用するコルーチンを追加する必要があるかもしれません。その場合は、coro1を待つコルーチンを作成し、それを使用して値を返します。

import asyncio
from random import randint


async def coro1():
    res = randint(0,3)
    await asyncio.sleep(res)
    print('coro1 finished with output {}'.format(res))
    return res

async def coro2():
    res = await coro1()
    res = res * res
    await asyncio.sleep(res)
    print('coro2 finished with output {}'.format(res))
    return res

async def main():
    await asyncio.gather(
        coro2(),
        coro2()
    ) # here we have two coroutines running parallely

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

出力:

coro1 finished with output 1
coro2 finished with output 1
coro1 finished with output 3
coro2 finished with output 9
[Finished in 12.2s]

コルーチンは、特定の構文を持つ通常の関数について考えてください。 (asyncio.gatherによって)いくつかの関数セットを並列に実行し、最初に実行した後に次の関数を開始できます。他の関数を呼び出す新しい関数を作成できます。

8

ここでの答えはどれも、質問に正確に答えているようには見えません。 「親」タスクに実行させることで、実行中のイベントループにタスクを追加することができます。親が子がすべて終了するまで終了しないことを確認するための最もPython的な方法はわかりません(それがあなたの望む動作だと仮定して)が、これは機能します。

import asyncio
import random


async def add_event(n):
    print('starting ' + str(n))
    await asyncio.sleep(n)
    print('ending ' + str(n))
    return n


async def main(loop):

    added_tasks = []

    delays = [x for x in range(5)]

    # shuffle to simulate unknown run times
    random.shuffle(delays)

    for n in delays:
        print('adding ' + str(n))
        task = loop.create_task(add_event(n))
        added_tasks.append(task)
        await asyncio.sleep(0)

    print('done adding tasks')

    # make a list of tasks that (maybe) haven't completed
    running_tasks = added_tasks[::]

    # wait until we see that all tasks have completed
    while running_tasks:
        running_tasks = [x for x in running_tasks if not x.done()]
        await asyncio.sleep(0)

    print('done running tasks')

    # extract the results from the tasks and return them
    results = [x.result() for x in added_tasks]
    return results


loop = asyncio.get_event_loop()
results = loop.run_until_complete(main(loop))
loop.close()
print(results)
1
Adam