2016-11-01 5 views
2

私はPython asyncioで遊んでいます。私のプログラムはコルーチンが3つしかありません。そのうちの2つは私が直接スケジュールし、3つ目はフォーマーの1人から予定されています。実際がキャンセルされたcoro2がキャンセルされなかった理由なぜ親コルーチンがキャンセルされないのですか?

coro1 
coro2 
coro3 
^CExiting... Cancelling all tasks 
Cancellation is done! 
coro3 exc CancelledError() 
coro1 exc CancelledError() 
Task was destroyed but it is pending! 
task: <Task pending coro=<coro2() running at test.py:15> wait_for=<Task cancelled coro=<coro3() done, defined at test.py:23>>> 

は、だから私は疑問に思う、とcoro3

import asyncio 

async def coro1(): 
    try: 
     print('coro1') 
     await asyncio.sleep(1000) 
    except Exception as e: 
     print('coro1 exc %s' % repr(e)) 
     raise 

async def coro2(): 
    try: 
     print('coro2') 
     await asyncio.ensure_future(coro3()) 
     await asyncio.sleep(1000) 
    except Exception as e: 
     print('coro2 exc %s' % repr(e)) 
     raise 

async def coro3(): 
    try: 
     print('coro3') 
     await asyncio.sleep(1000) 
    except Exception as e: 
     print('coro3 exc %s' % repr(e)) 
     raise 

loop = asyncio.get_event_loop()  
try: 
    f1 = asyncio.ensure_future(coro1()) 
    f2 = asyncio.ensure_future(coro2())  
    loop.run_forever() 
except KeyboardInterrupt: 
    print('Exiting... Cancelling all tasks') 

    f2.cancel() 
    f1.cancel() 

    # This code gives the same result: 
    # for task in asyncio.tasks.Task.all_tasks(loop): 
    # task.cancel() 

    print('Cancellation is done!') 

    loop.stop() 
    loop.run_forever() 
finally: 
    loop.close() 

このコードは次の出力を生成します。そして、私は、ユーザーがCtrl+Cが押したときに適切に私のプログラムを確定したいです?

+0

コルーチン2は、コルーチン3が復帰してからさらに1000秒後にスリープします。 – shongololo

+0

@ shongololo、私は '' await sleep(1000) ''をまったく削除しても、観察された振る舞いは変わらないだろうと思っています。 –

+0

ああ、私は(スクロールで隠された)あなたのコードサンプルの最低の行を見ませんでした。コルーチン2が例外を返すチャンスを得ておらず、ループが終了するまでにシャットダウンする可能性があります。あなたはループを閉じる前にすべてのタスクがキャンセルされていることを確認するのを待つことができますが、あなたの例外は実際には見えません... – shongololo

答えて

2

ありがとうございました!問題はexceptブロックに以下の2行にあった:

# ... 
loop.stop() 
loop.run_forever() 

予想キャンセル伝播が原因loop.stop()に動作しませんでした。 1は、このような何かにコードを変更した場合:

# ... 

try: 
    f1 = asyncio.ensure_future(coro1()) 
    f2 = asyncio.ensure_future(coro2())  
    loop.run_forever() 
except KeyboardInterrupt: 
    print('Exiting... Cancelling all tasks') 
    f2.cancel() 
    f1.cancel() 
    print('Cancellation is done!')  
    try: 
     loop.run_forever() 
     # Wait a very short time for f2 cancelation and press Ctrl+C again. 
    except KeyboardInterrupt: 
     loop.stop() 
     loop.run_forever() 
finally: 
    loop.close() 

メッセージTask was destroyed but it is pending!は離れて行くだろう。

f1 = asyncio.ensure_future(coro1()) 
f2 = asyncio.ensure_future(coro2()) 
tasks = asyncio.gather(f1, f2) 
try: 
    loop.run_until_complete(tasks) 
except KeyboardInterrupt: 
    print('Exiting... Cancelling all tasks') 
    tasks.cancel() # or f1.cancel(); f2.cancel() 
    print('Cancellation is done!') 

    loop.run_forever() 
    tasks.exception() # To skip '_GatheringFuture exception was never retrieved' warning 
finally: 
    loop.close() 

run_until_completeは、すべての作業を終えた(またはキャンセル)した後、ループを停止します内部のコールバックを追加します。

少しよりよい方法がloop.run_until_complete()アプローチを使用することでしょう。

関連する問題