简体   繁体   English

如何将协程添加到正在运行的异步循环中?

[英]how to add a coroutine to a running asyncio loop?

How can one add a new coroutine to a running asyncio loop?如何将新协程添加到正在运行的异步循环中? Ie. IE。 one that is already executing a set of coroutines.一个已经在执行一组协程的。

I guess as a workaround one could wait for existing coroutines to complete and then initialize a new loop (with the additional coroutine).我想作为一种解决方法,可以等待现有协程完成,然后初始化一个新循环(使用额外的协程)。 But is there a better way?但是有更好的方法吗?

You can use create_task for scheduling new coroutines:您可以使用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()

To add a function to an already running event loop you can use:要将函数添加到已经运行的事件循环中,您可以使用:

asyncio.ensure_future(my_coro())

In my case I was using multithreading ( threading ) alongside asyncio and wanted to add a task to the event loop that was already running.在我的例子中,我使用多线程 ( threading ) 和asyncio并想向已经运行的事件循环添加一个任务。 For anyone else in the same situation, be sure to explicitly state the event loop (as one doesn't exist inside a Thread ).对于处于相同情况的其他任何人,请务必明确说明事件循环(因为Thread中不存在事件循环)。 ie: IE:

In global scope:在全球范围内:

event_loop = asyncio.get_event_loop()

Then later, inside your Thread :然后,在你的Thread中:

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

Your question is very close to "How to add function call to running program?"您的问题非常接近“How to add function call to running program?”

When exactly you need to add new coroutine to event loop?究竟什么时候需要向事件循环中添加新协程?

Let's see some examples.让我们看一些例子。 Here program that starts event loop with two coroutines parallely:这里程序用两个协程并行启动事件循环:

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())

Output:输出:

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

May be you need to add some coroutines that would take results of coro1 and use it as soon as it's ready?可能您需要添加一些协程来获取coro1的结果并在准备就绪后立即使用它? In that case just create coroutine that await coro1 and use it's returning value:在那种情况下,只需创建等待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())

Output:输出:

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

Think about coroutines as about regular functions with specific syntax.将协同程序视为具有特定语法的常规函数。 You can start some set of functions to execute parallely (by asyncio.gather ), you can start next function after first done, you can create new functions that call others.您可以启动一组函数来并行执行(通过asyncio.gather ),您可以在第一次完成后启动下一个函数,您可以创建调用其他函数的新函数。

None of the answers here seem to exactly answer the question.这里的答案似乎都不能准确回答问题。 It is possible to add tasks to a running event loop by having a "parent" task do it for you.通过让“父”任务为您完成,可以将任务添加到正在运行的事件循环中。 I'm not sure what the most pythonic way to make sure that parent doesn't end until it's children have all finished (assuming that's the behavior you want), but this does work.我不确定什么是最 pythonic 的方式来确保父级在它的子级全部完成之前不会结束(假设这是你想要的行为),但这确实有效。

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 = list(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')

    results = await asyncio.gather(*added_tasks)
    print('done running tasks')

    return results


loop = asyncio.get_event_loop()
results = loop.run_until_complete(main(loop))
print(results)

If the task is to add a coroutine(s) to a loop that is already executing some coroutines, then you can use this solution of mine如果任务是将协程添加到已经在执行某些协程的循环中,那么您可以使用我的这个解决方案

import asyncio
import time
from threading import Thread

from random import randint


# first, we need a loop running in a parallel Thread
class AsyncLoopThread(Thread):
    def __init__(self):
        super().__init__(daemon=True)
        self.loop = asyncio.new_event_loop()

    def run(self):
        asyncio.set_event_loop(self.loop)
        self.loop.run_forever()


# example coroutine
async def coroutine(num, sec):
    await asyncio.sleep(sec)
    print(f'Coro {num} has finished')


if __name__ == '__main__':
    # init a loop in another Thread
    loop_handler = AsyncLoopThread()
    loop_handler.start()

    # adding first 5 coros
    for i in range(5):
        print(f'Add Coro {i} to the loop')
        asyncio.run_coroutine_threadsafe(coroutine(i, randint(3, 5)), loop_handler.loop)

    time.sleep(3)
    print('Adding 5 more coros')

    # adding 5 more coros
    for i in range(5, 10):
        print(f'Add Coro {i} to the loop')
        asyncio.run_coroutine_threadsafe(coroutine(i, randint(3, 5)), loop_handler.loop)

    # let them all finish
    time.sleep(60)
    

After execution of this example we will get this output:执行此示例后,我们将获得以下输出:

Add Coro 0 to the loop
Add Coro 1 to the loop
Add Coro 2 to the loop
Add Coro 3 to the loop
Add Coro 4 to the loop
Coro 0 has finished
Adding 5 more coros
Add Coro 5 to the loop
Add Coro 6 to the loop
Add Coro 7 to the loop
Add Coro 8 to the loop
Add Coro 9 to the loop
Coro 1 has finished
Coro 3 has finished
Coro 2 has finished
Coro 4 has finished
Coro 9 has finished
Coro 5 has finished
Coro 7 has finished
Coro 6 has finished
Coro 8 has finished

Process finished with exit code 0

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM