2016-11-01 5 views
2

Ich spiele mit Python asyncio. Mein Programm hat nur drei Korotinen. Zwei von ihnen plane ich direkt, während der dritte von einem der Former geplant wird. Und ich will mein Programm richtig fertig zu stellen, wenn der Benutzer drückt Ctrl+C:Warum wird die Eltern-Coroutine nicht abgebrochen?

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

Dieser Code die nächste Ausgabe erzeugt:

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

So frage ich mich, warum coro2 wurde nicht abgebrochen und coro3 tatsächlich wurde abgesagt ?

+0

Coroutine 2 schläft weitere 1000 Sekunden, nachdem Coroutine 3 zurückkehrt. Versuchen Sie mit einer kürzeren Dauer des Schlafes (oder warten Sie 1000s!) – shongololo

+0

@shongololo, ich fürchte, dass, selbst wenn ich "erwarten Schlaf (1000)" überhaupt löschen, das beobachtete Verhalten wird sich nicht ändern. –

+0

Ah, ich habe die untersten Zeilen Ihres Codebeispiels (durch Scrolling verborgen) nicht gesehen. Es kann sein, dass Coroutine 2 nicht die Möglichkeit hat, die Ausnahme zurückzugeben und herunterzufahren, wenn die Schleife geschlossen wird. Sie können warten, um zu überprüfen, dass alle Aufgaben abgebrochen werden, bevor Sie die Schleife schließen, aber dann werden Sie Ihre Ausnahmen in Aktion nicht sehen ... – shongololo

Antwort

2

Verstanden! Das Problem war in diesen beiden Linien im except Block:

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

Expected Abbruch Ausbreitung nicht wegen loop.stop() funktionierte. Wenn man den Code so etwas wie dies ändern:

# ... 

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

die Nachricht Task was destroyed but it is pending! weggehen würde.

Ein wenig schöner Weg wäre mit loop.run_until_complete() Ansatz:

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 fügt internen Rückruf, der die Schleife stoppt nach (oder Abbrechen), um alle Aufgaben beenden.

Verwandte Themen