2016-08-29 86 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")