【问题标题】:How to shutdown process with event loop and executor如何使用事件循环和执行程序关闭进程
【发布时间】:2018-08-29 15:20:39
【问题描述】:

考虑以下程序。

import asyncio
import multiprocessing
from multiprocessing import Queue
from concurrent.futures.thread import ThreadPoolExecutor
import sys


def main():
    executor = ThreadPoolExecutor()
    loop = asyncio.get_event_loop()
    # comment the following line and the shutdown will work smoothly
    asyncio.ensure_future(print_some(executor))

    try:
        loop.run_forever()
    except KeyboardInterrupt:
        print("shutting down")
        executor.shutdown()
        loop.stop()
        loop.close()
        sys.exit()


async def print_some(executor):
    print("Waiting...Hit CTRL+C to abort")
    queue = Queue()
    loop = asyncio.get_event_loop()
    some = await loop.run_in_executor(executor, queue.get)
    print(some)


if __name__ == '__main__':
    main()

我想要的只是当我按下“CTRL+C”时优雅地关机。但是,执行线程似乎阻止了这种情况(即使我确实调用了shutdown

【问题讨论】:

  • 你是什么意思“#注释下面一行,关机就会顺利”?如果您评论该行,则您在执行程序中什么也不做。你是什​​么意思“优雅关机”? shutdown 会加入每一个工人,是不是很优雅?
  • 哦,也许我理解你,因为queue.get 是一个阻塞操作,所以shutdown 被永远挂起。您应该先向queue 发送一个信号,表明它已经结束。

标签: python python-multithreading python-asyncio


【解决方案1】:

您需要发送毒丸来让工作人员停止监听 queue.get 调用。 ThreadPoolExecutor 池中的工作线程如果有活动的工作,将阻止 Python 退出。有一个 comment in the source code 描述了这种行为的原因:

# Workers are created as daemon threads. This is done to allow the interpreter
# to exit when there are still idle threads in a ThreadPoolExecutor's thread
# pool (i.e. shutdown() was not called). However, allowing workers to die with
# the interpreter has two undesirable properties:
#   - The workers would still be running during interpreter shutdown,
#     meaning that they would fail in unpredictable ways.
#   - The workers could be killed while evaluating a work item, which could
#     be bad if the callable being evaluated has external side-effects e.g.
#     writing to a file.
#
# To work around this problem, an exit handler is installed which tells the
# workers to exit when their work queues are empty and then waits until the
# threads finish.

这是一个干净退出的完整示例:

import asyncio
import multiprocessing
from multiprocessing import Queue
from concurrent.futures.thread import ThreadPoolExecutor
import sys


def main():
    executor = ThreadPoolExecutor()
    loop = asyncio.get_event_loop()
    # comment the following line and the shutdown will work smoothly
    fut = asyncio.ensure_future(print_some(executor))

    try:
        loop.run_forever()
    except KeyboardInterrupt:
        print("shutting down")
        queue.put(None)  # Poison pill
        loop.run_until_complete(fut)
        executor.shutdown()
        loop.stop()
        loop.close()


async def print_some(executor):
    print("Waiting...Hit CTRL+C to abort")
    loop = asyncio.get_event_loop()
    some = await loop.run_in_executor(executor, queue.get)
    print(some)


queue = None
if __name__ == '__main__':
    queue = Queue()
    main()

需要run_until_complete(fut) 调用以避免在异步事件循环退出时出现关于挂起任务的警告。如果您不关心这一点,则可以忽略该调用。

【讨论】:

  • 啊!谢谢你。因此,基本上,如果您在 ThreadPoolExecutor 中执行某些操作,您需要将其设计为可停止的(例如通过某些条件),以便完成工作以最终让守护程序关闭。
  • @Christoph 是的,没错。
  • 不知道有没有办法联系你。除了在 aioprocessing 上滥用 github 问题;)电子邮件/推特?
猜你喜欢
  • 2016-07-12
  • 2015-01-27
  • 1970-01-01
  • 1970-01-01
  • 2021-03-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多