【问题标题】:python asyncio cancel run_in_executor blocks mainthreadpython asyncio cancel run_in_executor 阻塞主线程
【发布时间】:2021-12-14 00:39:57
【问题描述】:

通过 asyncio run_in_executor 启动同步功能然后取消它时,我看到了不想要的行为。 ThreadPoolExecutor(以及 ProcessPoolExecutor)上下文管理器将在退出时调用 executor.shutdown 并等待所有待处理的工作完成(在其线程上使用 join())。

我原以为连接是异步管理的,所以其他任务可能不会被阻塞,但我发现连接是同步调用的。

run_in_executor 的实现应该考虑这个吗?还是应该处理一些事情(比如在线程上调用 executor.shutdown)?

此示例代码启动了2个任务,并取消了在子线程上运行的一个,从日志中可以看出,子线程是取消后唯一正在执行的代码:

from time import sleep
from asyncio import (
    get_running_loop, 
    sleep as async_sleep,
    get_event_loop,
    create_task
)
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime
​
​
def sync_watchdog():
    for _ in range(30):
        print(f'{datetime.now()} I am in a thread')
        sleep(1)
​
​
async def inthread():
    with ThreadPoolExecutor() as executor:
        await get_running_loop().run_in_executor(executor, sync_watchdog)
​
async def watchdog():
    for _ in range(30):
        print(f'{datetime.now()} I am an async watchdog')
        await async_sleep(1)
​
​
async def run():
    intread_task = create_task(inthread())
    watchdog_task = create_task(watchdog())
​
    await async_sleep(2)
​
    print('canceling task with subthread')
    intread_task.cancel()
​
    await async_sleep(10)
​
​
get_event_loop().run_until_complete(run())

这将输出:

2021-10-27 17:08:16.796171 I am in a thread
2021-10-27 17:08:16.796248 I am an async watchdog
2021-10-27 17:08:17.797205 I am an async watchdog
2021-10-27 17:08:17.797310 I am in a thread
canceling task with subthread
2021-10-27 17:08:18.797990 I am an async watchdog
2021-10-27 17:08:18.798430 I am in a thread
2021-10-27 17:08:19.799037 I am in a thread
2021-10-27 17:08:20.800327 I am in a thread
2021-10-27 17:08:21.801521 I am in a thread
2021-10-27 17:08:22.802819 I am in a thread
2021-10-27 17:08:23.804014 I am in a thread
2021-10-27 17:08:24.805434 I am in a thread
2021-10-27 17:08:25.806640 I am in a thread
2021-10-27 17:08:26.807797 I am in a thread
2021-10-27 17:08:27.808880 I am in a thread
2021-10-27 17:08:28.810305 I am in a thread
2021-10-27 17:08:29.811247 I am in a thread
2021-10-27 17:08:30.811901 I am in a thread
2021-10-27 17:08:31.813083 I am in a thread
2021-10-27 17:08:32.814301 I am in a thread
2021-10-27 17:08:33.815499 I am in a thread
2021-10-27 17:08:34.816565 I am in a thread
2021-10-27 17:08:35.817920 I am in a thread
2021-10-27 17:08:36.818478 I am in a thread
2021-10-27 17:08:37.819603 I am in a thread
2021-10-27 17:08:38.820844 I am in a thread
2021-10-27 17:08:39.822042 I am in a thread
2021-10-27 17:08:40.822491 I am in a thread
2021-10-27 17:08:41.823690 I am in a thread
2021-10-27 17:08:42.824871 I am in a thread
2021-10-27 17:08:43.826012 I am in a thread
2021-10-27 17:08:44.826516 I am in a thread
2021-10-27 17:08:45.827698 I am in a thread
2021-10-27 17:08:46.829813 I am an async watchdog

【问题讨论】:

  • 您不能使用 asyncio 关闭同步函数,无论该函数在哪个线程中。Python 线程的一个基本属性是它们不能在外部关闭 - 它们会一直持续到运行() 方法终止。

标签: python python-asyncio concurrent.futures


【解决方案1】:

长话短说:.shutdown() 仅取消 待处理 执行程序任务,尚未运行。

那是因为threading.Thread 没有.cancel().abort() 方法。如果您想在正在运行的线程中停止循环,请创建一个标志,在每次迭代中检查它,如果您需要停止,请提高标志。

【讨论】:

  • 是的,没关系。我还喜欢 shutdown() 上最新的“cancel_futures”,它将取消未提交的任务。但是,我觉得奇怪的是 join() 是在主线程上完成的,阻塞了主异步循环并阻止其他任务运行。我想知道是否应该以不同的方式处理,以便可以“异步”完成 join()
  • 我很好奇为什么需要动态执行器?通常,我会在程序开始时创建所有需要的执行程序,并在程序退出之前完成它们
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-25
  • 2018-02-16
  • 1970-01-01
  • 2015-12-29
相关资源
最近更新 更多