asyncio.wait_for超时后如何清理?

时间:2019-06-15 15:08:17

标签: python python-3.x python-asyncio

我的目标是使用asyncio库进行一些练习。我已经阅读了一些入门教程,现在我想自己写一些代码。

我想开始两个简单的任务,它们基本上增加了存储在外部类中的公共值。第一个是自动的-5秒后增加1。第二项任务与用户相关:如果您在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()

1 个答案:

答案 0 :(得分:1)

您已经在threadpoolexecutor和那些can't actually be cancelled中启动了一个单独的线程。任务asyncio'委托'被取消,但是sys.stdin.readline调用将无限期地坐在那里。您可以按Enter键结束它,因为这样可以在sys.stdin上显示完整的一行。

您必须在此处使用one of the work-arounds to cancel the read;请注意,您不能告诉ThreadPoolExecutor使用守护程序线程。

在异步环境中将用户输入作为单独的任务等待时,仅创建自己的线程而不是要求THreadPoolExecutor为您管理线程可能会更容易,因此您可以设置{ {1}}在该线程上,只是让进程在退出时杀死该线程。