2016-08-29 61 views
1

我在这个python asyncio主题中很新。我有一个简单的问题: 我有一个任务包含两个同时运行的协程。第一个协程(my_coroutine)只会持续打印一些东西,直到达到second_to_sleep。第二个协程(seq_coroutine)会依次调用4个其他协程。我的目标是在seq_coroutine完成时停止循环。确切地说,我希望my_coroutine在seq_coroutine完成之前一直活着。有人可以帮助我吗?Python asyncio:当一个协程完成时停止循环

我的代码是这样的:

import asyncio 

async def my_coroutine(task, seconds_to_sleep = 3): 
    print("{task_name} started\n".format(task_name=task)) 
    for i in range(1, seconds_to_sleep): 
     await asyncio.sleep(1) 
     print("\n{task_name}: second {seconds}\n".format(task_name=task, seconds=i)) 

async def coroutine1(): 
    print("coroutine 1 started") 
    await asyncio.sleep(1) 
    print("coroutine 1 finished\n") 


async def coroutine2(): 
    print("coroutine 2 started") 
    await asyncio.sleep(1) 
    print("coroutine 2 finished\n") 


async def coroutine3(): 
    print("coroutine 3 started") 
    await asyncio.sleep(1) 
    print("coroutine 3 finished\n") 


async def coroutine4(): 
    print("coroutine 4 started") 
    await asyncio.sleep(1) 
    print("coroutine 4 finished\n") 


async def seq_coroutine(): 
    await coroutine1() 
    await coroutine2() 
    await coroutine3() 
    await coroutine4() 

def main(): 
    main_loop = asyncio.get_event_loop() 
    task = [asyncio.ensure_future(my_coroutine("task1", 11)), 
      asyncio.ensure_future(seq_coroutine())] 
    try: 
     print('loop is started\n') 
     main_loop.run_until_complete(asyncio.gather(*task)) 
    finally: 
     print('loop is closed') 
     main_loop.close() 


if __name__ == "__main__": 
    main() 

这是该程序的输出:

loop is started 

task1 started 

coroutine 1 started 

task1: second 1 

coroutine 1 finished 
coroutine 2 started 

task1: second 2 

coroutine 2 finished 
coroutine 3 started 

task1: second 3 

coroutine 3 finished 
coroutine 4 started 

task1: second 4 

coroutine 4 finished 

task1: second 5 
task1: second 6 
task1: second 7 
task1: second 8 
task1: second 9 
task1: second 10 

loop is closed 

我只希望有这样的事情:

loop is started 

task1 started 

coroutine 1 started 

task1: second 1 

coroutine 1 finished 
coroutine 2 started 

task1: second 2 

coroutine 2 finished 
coroutine 3 started 

task1: second 3 

coroutine 3 finished 
coroutine 4 started 

task1: second 4 

coroutine 4 finished 

loop is closed 
+1

为什么不'run_until_complete(seq_coroutine)'? – dirn

+0

同时做这件事的想法不仅仅是睡觉和打印。 “my_coroutine”是一个监听别的东西的过程,应该和seq_coroutine并行运行,但为了简单起见,我只是简化它。 – parsa

回答

5

我只是为我的问题找到了合适的解决方案。 我不会删除我的帖子,我会发布我的解决方案,以便它可以帮助其他面临同样问题的人。 我使用了asyncio.wait(task, return_when=asyncio.FIRST_COMPLETED),它会在第一个任务完成时返回结果。 这是解决方案:

import asyncio 
from asyncio.tasks import FIRST_COMPLETED 
from concurrent.futures import CancelledError 

async def my_coroutine(task, seconds_to_sleep = 3): 
    print("{task_name} started\n".format(task_name=task)) 
    for i in range(1, seconds_to_sleep): 
     await asyncio.sleep(1) 
     print("\n{task_name}: second {seconds}\n".format(task_name=task, seconds=i)) 

async def coroutine1(): 
    print("coroutine 1 started") 
    await asyncio.sleep(1) 
    print("coroutine 1 finished\n") 


async def coroutine2(): 
    print("coroutine 2 started") 
    await asyncio.sleep(1) 
    print("coroutine 2 finished\n") 


async def coroutine3(): 
    print("coroutine 3 started") 
    await asyncio.sleep(1) 
    print("coroutine 3 finished\n") 


async def coroutine4(): 
    print("coroutine 4 started") 
    await asyncio.sleep(1) 
    print("coroutine 4 finished\n") 


async def seq_coroutine(loop): 
    await coroutine1() 
    await coroutine2() 
    await coroutine3() 
    await coroutine4() 

def main(): 
    main_loop = asyncio.get_event_loop() 
    task = [asyncio.ensure_future(my_coroutine("task1", 11)), 
      asyncio.ensure_future(seq_coroutine(main_loop))] 
    try: 
     print('loop is started\n') 
     done, pending = main_loop.run_until_complete(asyncio.wait(task, return_when=asyncio.FIRST_COMPLETED)) 
     print("Completed tasks: {completed}\nPending tasks: {pending}".format(completed = done, pending = pending)) 

     #canceling the tasks 
     for task in pending: 
      print("Cancelling {task}: {task_cancel}".format(task=task, task_cancel=task.cancel())) 

    except CancelledError as e: 
     print("Error happened while canceling the task: {e}".format(e=e)) 
    finally: 
     print('loop is closed') 


if __name__ == "__main__": 
    main() 
0

您可以使用一个变量来通知另一个协程。 asyncio.Event通常用于:

import asyncio 

import random 


async def clock(name, event): 
    print("* {} started".format(name)) 
    i = 0 
    while not event.is_set(): 
     await asyncio.sleep(0.1) 
     i += 1 
     print("* {}: {}".format(name, i)) 
    print("* {} done".format(name)) 
    return i 


async def coro(x): 
    print("coro() started", x) 
    await asyncio.sleep(random.uniform(0.2, 0.5)) 
    print("coro() finished", x) 


async def seq_coroutine(name): 
    event = asyncio.Event() 
    clock_task = asyncio.ensure_future(clock(name, event)) 
    # await asyncio.sleep(0) # if you want to give a chance to clock() to start 
    await coro(1) 
    await coro(2) 
    await coro(3) 
    await coro(4) 
    event.set() 
    i = await clock_task 
    print("Got:", i) 


def main(): 
    main_loop = asyncio.get_event_loop() 
    main_loop.run_until_complete(seq_coroutine("foo")) 
    main_loop.close() 


if __name__ == "__main__": 
    main() 

您还可以使用await event.wait()直到事件被设置为阻止一段代码:

async def xxx(event): 
    print("xxx started") 
    await event.wait() 
    print("xxx ended")