我有两个协同例程。一个处理到websocket服务器的连接。另一个人做了一些武断的事情。发生异常时,我想终止程序。然而,处理与websocket服务器连接的协同例程仍在运行
请参阅此示例(不需要任何websocket服务器,只需运行此代码,它就会尝试重新连接到服务器(:
import asyncio
import websockets
async def task1():
""" A dummy co-routine
After five iterations. raise an exception"""
i = 1
while True:
print(f"In task 1 iteration {i}")
if i == 5:
raise Exception("Terminate task 1")
i += 1
await asyncio.sleep(1)
async def task2():
""" Connect to websocket server"""
i = 1
while True:
print(f"Attempt {i} to connect to server")
try:
await websockets.connect("ws://localhost:9090")
except Exception as e:
print(e)
else:
break
i += 1
await asyncio.sleep(1)
async def main():
tasks = [task1(), task2()]
finished, unfinished = await asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION)
for task in finished:
try:
await task
except Exception as e:
print("Exception detected:", repr(e))
if __name__ == "__main__":
print("Start")
asyncio.run(main())
print("End")
输出为:
Start
In task 1 iteration 1
Attempt 1 to connect to server
In task 1 iteration 2
In task 1 iteration 3
Multiple exceptions: [Errno 10061] Connect call failed ('127.0.0.1', 9090), [Errno 10061] Connect call failed ('::1', 9090, 0, 0)
In task 1 iteration 4
Attempt 2 to connect to server
In task 1 iteration 5
Exception detected: Exception('Terminate task 1')
Attempt 3 to connect to server
Multiple exceptions: [Errno 10061] Connect call failed ('127.0.0.1', 9090), [Errno 10061] Connect call failed ('::1', 9090, 0, 0)
Attempt 4 to connect to server
Multiple exceptions: [Errno 10061] Connect call failed ('127.0.0.1', 9090), [Errno 10061] Connect call failed ('::1', 9090, 0, 0)
Attempt 5 to connect to server
这里怎么了
我使用的是python 3.7.7。websockets版本是8.1。
更新
我安装了python 3.8.2,结果不一样。一旦出现异常,应用程序就会终止。这是输出:
Start
Attempt 1 to connect to server
In task 1 iteration 1
In task 1 iteration 2
In task 1 iteration 3
[WinError 1225] The remote computer refused the network connection
In task 1 iteration 4
Attempt 2 to connect to server
In task 1 iteration 5
Exception detected: Exception('Terminate task 1')
End
对此有什么解释吗?
- 在
task2()
中重新引发Cancelled错误。否则,任务不会被取消main()
退出时,asyncio.run
将尝试取消所有正在运行的任务,并等待它们完成。except Exception
语句捕获引发的CancelledError,并导致asyncio.run
中的永久等待
以下是问题的可选选项,尽管它们是良好的实践。
- 使用
asyncio.create_task()
或asyncio.ensure_future()
包装协程对象,将其视为类似未来的对象(任务将在事件循环中调度( - 反复检查未完成的任务并取消它们
- 优选地,
await
对取消的任务查看任何其他错误
async def task2():
""" Connect to websocket server"""
i = 1
while True:
print(f"Attempt {i} to connect to server")
try:
await websockets.connect("ws://localhost:9090")
except asyncio.CancelledError:
raise
except Exception as e:
print(e)
i += 1
await asyncio.sleep(1)
async def main():
tasks = [
asyncio.create_task(task1()),
asyncio.create_task(task2()),
]
finished, unfinished = await asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION)
for task in finished:
try:
await task
except Exception as e:
print("Exception detected:", repr(e))
for task in unfinished:
task.cancel()
for task in unfinished:
try:
await task.result()
except asyncio.CancelledError:
pass
except Exception as e:
print("Exception detected:", repr(e))