2016-09-06 19 views
5

Mam asyncio programu/Python z dwoma asyncio zadań:Python asyncio programu nie wypłynie

  • taki, który rozbija
  • , który trwa przez wieki.

Chcę, aby mój cały program zakończył się po pierwszej awarii. Nie mogę tego zrobić.

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

Drukuje BŁĄD, ale nie wychodzi. Przy ręcznym zamknięciu program drukuje następujące ślad stosu:

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

Czy próbowałeś wyjść ze współpracy? Prawdopodobnie nie to, czego chcesz, ale przynajmniej jako eksperyment, możesz: zmienić pętlę w 'infinite_while', aby wypowiedzieć' while not exit_requested'; zmień 'task_1', aby wychwycić wyjątek asercji, ustawić flagę i przebić; i zobacz, czy wywołanie funkcji exit() kończy się po zakończeniu każdego zadania, jeden zwykle i jeden z wyjątkiem potwierdzenia. –

+0

Dzięki, działa, użyje go w ostateczności, ale ma nadzieję, że istnieje czystszy sposób rozwiązania. – MrJ

Odpowiedz

1

gdy wyjątek powstał w zadaniu nie propaguje się do zakresu, w którym zadanie inicjowane eventloop, to znaczy, wywołanie loop.run_until_complete(tasks). Pomyśl o tym, jakby wyjątek został zgłoszony tylko w kontekście twojego zadania, a to jest zasięg najwyższego poziomu, na którym masz szansę go obsłużyć, w przeciwnym razie zostanie on podniesiony na "tle".

Ten powiedział, że nigdy nie będzie złapać Exception z zadania z tego:

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

... i to jest po prostu jak działa pętla zdarzeń. Wyobraź sobie, że jeśli masz usługę z kilkoma zadaniami, a jedna z nich zawiedzie, to zatrzyma całą usługę.

Możliwe jest ręczne wykonanie zdarzenia stop podczas wywołania wyjątku w task1, np.

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

Jest to jednak bardzo brudny i rodzaj hacky, więc sugeruję raczej iść z rozwiązania, które @D-Von suggested, która jest znacznie czystsze i bezpieczniejsze.

+0

Mam przypadek użycia, gdzie chcę dokładnie to. Pętla zdarzeń jest uruchamiana i zatrzymywana dla małej części kodu, w której potrzebuję współbieżności. Jeśli wystąpi wyjątek, wolałbym, gdyby cała pętla zdarzeń została zatrzymana, a program został zamknięty. W pętli zdarzeń mam kilka długotrwałych, ale nieskończenie uruchomionych procesów, i chciałbym je anulować, jeśli wystąpi jeden wyjątek. Moja percepcja jest taka, że ​​asyncio jest zaprojektowane dla świata, w którym pętla zdarzeń jest zawsze uruchomiona, ale jest także dobrą biblioteką dla różnych przypadków użycia - jeśli obsługa wyjątków była bardziej przejrzysta. –