2016-09-06 25 views
5

tôi có một chương trình/Python asyncio với hai nhiệm vụ asyncio:Python asyncio chương trình sẽ không thoát khỏi

  • một mà treo
  • một mà đi vào mãi mãi.

Tôi muốn toàn bộ chương trình của mình thoát sau lần gặp đầu tiên. Tôi không thể làm cho nó xảy ra.

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

Nó in ERROR nhưng không thoát. Khi đóng bằng tay, chương trình in stack trace sau:

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

Bạn đã thử hợp tác thoát? Có lẽ không phải những gì bạn muốn, nhưng ít nhất là một thử nghiệm, bạn có thể: thay đổi vòng lặp trong 'infinite_while' để nói' while not exit_requested'; thay đổi 'task_1' để bắt ngoại lệ khẳng định, thiết lập cờ và reraise; và xem cuộc gọi của bạn để thoát() có hoàn thành sau khi mỗi tác vụ đã thoát, một bình thường và một với ngoại lệ khẳng định. –

+0

Cảm ơn, nó hoạt động, sẽ sử dụng nó như một phương sách cuối cùng nhưng hy vọng có một cách sạch hơn để giải quyết nó. – MrJ

Trả lời

1

Khi một ngoại lệ được tăng lên trong một nhiệm vụ, nó không bao giờ truyền đến phạm vi nơi công việc đã được đưa ra thông qua eventloop, ví dụ: các loop.run_until_complete(tasks) gọi. Hãy nghĩ về nó, như thể trường hợp ngoại lệ chỉ được ném trong bối cảnh nhiệm vụ của bạn, và đó là phạm vi cấp cao nhất mà bạn có cơ hội để xử lý nó, nếu không nó sẽ được tăng lên trong "nền".

này cho biết, bạn sẽ không bao giờ bắt một Exception từ nhiệm vụ với điều này:

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

... và đó chỉ là cách thức hoạt động vòng lặp sự kiện. Hãy tưởng tượng nếu bạn sẽ có một dịch vụ với một số nhiệm vụ, và một trong số họ sẽ thất bại, điều đó sẽ ngừng toàn bộ dịch vụ.

Điều bạn có thể làm là stop vòng lặp theo cách thủ công khi bạn bắt ngoại lệ trong task1, ví dụ:

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

Tuy nhiên, điều này rất dơ bẩn và loại hacky, vì vậy tôi thà gợi ý để đi với các giải pháp mà @D-Von suggested, đó là sạch hơn và an toàn hơn.

+0

Tôi có một trường hợp sử dụng mà tôi muốn chính xác điều đó. Vòng lặp sự kiện được bắt đầu và dừng lại cho một phần nhỏ của mã mà tôi cần đồng thời. Nếu một ngoại lệ xảy ra, tôi muốn nếu toàn bộ vòng lặp sự kiện dừng lại, và chương trình thoát. Trong vòng lặp sự kiện, tôi có một số quy trình chạy dài, nhưng không chạy vô hạn, và tôi muốn hủy bỏ tất cả nếu một ngoại lệ xảy ra. Nhận thức của tôi là asyncio được thiết kế cho một thế giới mà eventloop luôn chạy, nhưng nó cũng là một thư viện tốt cho các trường hợp sử dụng khác nhau - nếu xử lý ngoại lệ rõ ràng hơn. –

Các vấn đề liên quan