python - asyncio.wait_for 超时后如何清理?
问题描述
我的目标是练习使用 asyncio 库。我已经阅读了一些介绍性教程,现在我想自己编写一些代码。
我想开始两个简单的任务,它们基本上增加了存储在外部类中的公共值。第一个是自动的 - 5 秒后增加一。第二个任务是用户相关的:如果你在这 5 秒内输入了一些值,它也应该被添加。
问题是,当我不输入任何值时,我的循环不会关闭 - 程序仍然处于活动状态并永远运行,直到我强制停止它 - 然后我收到以下错误:
2.py
[Auto_increment: ] This task will increment value after 5 seconds
[Manual increment: ] Waiting 5s for inc value:
Timeout
Loop finished. Value is 1
Error in atexit._run_exitfuncs:
Traceback (most recent call last):
File "/usr/lib/python3.7/concurrent/futures/thread.py", line 40, in _python_exit
t.join()
File "/usr/lib/python3.7/threading.py", line 1032, in join
self._wait_for_tstate_lock()
File "/usr/lib/python3.7/threading.py", line 1048, in _wait_for_tstate_lock
elif lock.acquire(block, timeout):
KeyboardInterrupt
Process finished with exit code 0
基本上在“循环完成”之后程序结束,但是当控制台输入没有输入值时,程序就会挂起。当我输入任何 v
2.py
[Auto_increment: ] This task will increment value after 5 seconds
[Manual increment: ] Waiting 5s for inc value:
5
Loop finished. Value is 6
Process finished with exit code 0
看起来当 TimeoutError 发生时,在 asyncio.wait_for 之后没有清理一些东西。你能帮我看看,有什么问题吗?这是我的代码:
import asyncio
import sys
class ValContainer:
_val = 0
@staticmethod
def inc_val(how_many=1):
ValContainer._val += how_many
@staticmethod
def get_val() -> int:
return ValContainer._val
async def auto_increment():
print(f'[Auto_increment: ] This task will increment value after 5 seconds')
await asyncio.sleep(5)
ValContainer.inc_val()
return True
async def manual_increment(loop):
print(f'[Manual increment: ] Waiting 5s for inc value:')
try:
future = loop.run_in_executor(None, sys.stdin.readline)
line = await asyncio.wait_for(future, 5, loop=loop)
if line:
try:
how_many = int(line)
ValContainer.inc_val(how_many)
except ValueError:
print('That\'s not a number!')
except asyncio.TimeoutError:
print('Timeout')
finally:
return True
if __name__ == '__main__':
loop = asyncio.get_event_loop()
task_auto = loop.create_task(auto_increment())
task_man = loop.create_task(manual_increment(loop))
loop.run_until_complete(task_auto)
loop.run_until_complete(task_man)
print(f'Loop finished. Value is {ValContainer.get_val()}')
loop.close()
解决方案
您已经在线程池执行程序中启动了一个单独的线程,而这些线程实际上不能被取消。任务“asyncio
委托”被取消,但sys.stdin.readline
呼叫将无限期地坐在那里。您可以通过按 Enter 来结束它,因为这会给您一个完整的sys.stdin
.
您必须使用其中一种解决方法来取消此处的读取;请注意,您不能告诉ThreadPoolExecutor
使用守护线程。
在等待用户输入作为异步上下文中的单独任务的情况下,创建自己的线程可能更容易,而不是要求 aTHreadPoolExecutor
为您管理线程,因此您可以daemon=True
在该线程上设置并让进程终止退出时的那个线程。
推荐阅读
- generics - 异步迭代 BTreeSet 时出现奇怪的生命周期错误
- keycloak - 如何在安装 bitnami/keycloak helm 图表时传递 -Dkeycloak.profile.feature.upload_scripts=enabled 标志
- c - 这个函数中的参数是如何排序的?
- c - 用标准(便携式)C 方法替换紧凑结构的“__attribute__ ((packed))”
- reactjs - 如何调试请求的资源上不存在“Access-Control-Allow-Origin”标头
- java - 如何从 Swagger 更新 Postman 集合
- javascript - IONIC 4:插件 firebase 和 google-maps 的问题 - 默认 FirebaseApp 未在此进程 com.superagent.agent 中初始化。一定要打电话
- javascript - React 中的单选按钮“onChange”切换
- sapui5 - 引导 ushell_abap 以实现变体持久性
- c# - 直接在 Blazor 页面中编写 Blazor 组件 HTML 元素的子元素