2016-09-06 3 views
5

verlassen Ich habe ein asyncio/Python-Programm mit zwei asyncio Aufgaben:Python asyncio Programm nicht

  • eine, die
  • man stürzt, die immer weitergeht.

Ich möchte mein gesamtes Programm nach dem ersten Absturz beenden. Ich kann es nicht passieren.

import asyncio 
import time 

def infinite_while(): 
    while True: 
     time.sleep(1) 


async def task_1(): 
    await asyncio.sleep(1) 
    assert False 


async def task_2(): 
    loop = asyncio.get_event_loop() 
    await loop.run_in_executor(None, lambda: infinite_while()) 


loop = asyncio.get_event_loop() 
asyncio.set_event_loop(loop) 

tasks = asyncio.gather(task_2(), task_1()) 
try: 
    loop.run_until_complete(tasks) 
except (Exception, KeyboardInterrupt) as e: 
    print('ERROR', str(e)) 
    exit() 

Es wird Fehler ausgegeben, aber nicht beendet. Wenn manuell geschlossen, druckt das Programm den folgenden Stack-Trace:

Error in atexit._run_exitfuncs: 
Traceback (most recent call last): 
    File "/usr/lib/python3.5/concurrent/futures/thread.py", line 39, in _python_exit 
    t.join() 
    File "/usr/lib/python3.5/threading.py", line 1054, in join 
    self._wait_for_tstate_lock() 
    File "/usr/lib/python3.5/threading.py", line 1070, in _wait_for_tstate_lock 
    elif lock.acquire(block, timeout): 
KeyboardInterrupt 
+2

Haben Sie versucht, kooperativ zu beenden? Wahrscheinlich nicht, was Sie wollen, aber zumindest als Experiment könnten Sie: die Schleife in 'infinite_while' ändern, um' while not exit_requested' zu sagen; Ändern Sie 'task_1', um die Assertion-Ausnahme abzufangen, setzen Sie das Flag und reraise; und sehen Sie, ob Ihr Aufruf von exit() beendet wird, sobald jeder Task beendet wurde, einen normalen und einen mit der Assertion-Ausnahme. –

+0

Danke, es funktioniert, wird es als letzten Ausweg nutzen, aber ich hoffe, es gibt einen saubereren Weg, es zu lösen. – MrJ

Antwort

1

Wenn eine Ausnahme in einer Aufgabe aufgegangen ist, ist es nie auf den Umfang ausbreitet, wo die Aufgabe über Ereignisschleife gestartet wurde, das heißt, den loop.run_until_complete(tasks) Anruf. Denken Sie daran, als ob die Ausnahme nur im Kontext Ihrer Aufgabe ausgelöst wird, und das ist der Bereich der höchsten Ebene, wo Sie die Chance haben, damit umzugehen, andernfalls wird es im "Hintergrund" erhöht.

Das heißt, Sie werden nie eine Exception von der Aufgabe, mit diesem fangen:

try: 
    loop.run_until_complete(tasks) 
except (Exception, KeyboardInterrupt) as e: 
    print('ERROR', str(e)) 
    exit() 

... und das ist nur, wie die Ereignisschleife funktioniert. Stellen Sie sich vor, Sie hätten einen Dienst mit mehreren Aufgaben, und einer von ihnen würde scheitern, das würde den gesamten Dienst stoppen.

Was Sie tun könnten, ist stop Eventloop manuell, wenn Sie eine Ausnahme in , z.

async def task_1(): 
    await asyncio.sleep(1) 
    try: 
     assert False 
    except Exception: 
     # get the eventloop reference somehow 
     eventloop.stop() 

Dies ist jedoch sehr schmutzig und Art von Hacky, also würde ich eher mit der Lösung, die @D-Von suggested, gehen vorzuschlagen, die viel sauberer und sicherer ist.

+0

Ich habe einen Anwendungsfall, wo ich genau das möchte. Die Ereignisschleife wird für einen kleinen Teil des Codes, für den ich Parallelität benötige, gestartet und gestoppt. Wenn eine Ausnahme auftritt, würde ich es vorziehen, wenn die gesamte Ereignisschleife gestoppt wird und das Programm beendet wird. In der Ereignisschleife habe ich mehrere langwierige, aber nicht unendlich laufende Prozesse, und ich möchte sie alle abbrechen, wenn eine Ausnahme auftritt. Meine Wahrnehmung ist, dass asyncio für eine Welt entworfen wurde, in der die Eventloop immer läuft, aber es ist auch eine gute Bibliothek für verschiedene Anwendungsfälle - wenn die Ausnahmebehandlung transparenter ist. –