【问题标题】:Python multiprocessing processes instantly die after being startedPython多处理进程在启动后立即死亡
【发布时间】:2020-12-04 11:46:21
【问题描述】:

我正在努力使用 Python“多处理”模块。我想用简单的东西填充队列并在队列不为空时打印东西,这会让我知道我的一个进程已终止。

您会在下面找到一个最小的示例:

  1. 我创建了一个空队列q

  2. 我创建了一个进程列表processes,只要我的 CPU 允许我就可以;我一创建就启动列表中的每个进程 (proc.start())

  3. 每个进程的目标函数是一个很简单的函数f,它先等待2秒,然后在队列中写入'hello'(这是它唯一的参数)

  4. 之后,我每 0.5 秒检查一次 q 是否不为空(每当我的一个进程成功执行函数 f 时,它应该收到一个“你好”),并且我还检查我的进程是否处于活动状态或不是。

我在这里面临两个问题:

  • 从第一次尝试开始,我的所有进程都活着;
  • 从未打印任何内容,这意味着队列q 从未成功接收到任何“hello”。

你会在下面找到我的代码。

# -*- coding: utf-8 -*-
"""
Created on Fri Dec  4 12:21:23 2020

@author: rbourgeon
"""
import multiprocessing as mp
import time


def f(q):
    time.sleep(2)
    q.put('hello')


pool_size = mp.cpu_count() - 1
print(f'pool_size is {pool_size}')

q = mp.Queue()

processes = []
num_active_processes = 0

# Starting processes
while len(processes) < pool_size:
    proc = mp.Process(target=f,
                      args=(q,)
                      )   
    processes.append(proc)
    proc.start()
    print(f'{len(processes)} jobs started')
    num_active_processes += 1

# Checking if queue is empty every 0.5 second. If not empty, we pop an element
# and we print it
    
for i in range(1, 100):
    print(f'\nAttempt #{i}')
    if not q.empty():
        print(q.get())
    time.sleep(0.5)
    print(processes)
    print([p.is_alive() for p in processes])

以下内容被打印到控制台:

pool_size is 7
1 jobs started
2 jobs started
3 jobs started
4 jobs started
5 jobs started
6 jobs started
7 jobs started

Attempt #1
[<Process(Process-8, stopped[1])>, <Process(Process-9, stopped[1])>, <Process(Process-10, stopped[1])>, <Process(Process-11, stopped[1])>, <Process(Process-12, stopped[1])>, <Process(Process-13, stopped[1])>, <Process(Process-14, stopped[1])>]
[False, False, False, False, False, False, False]

Attempt #2
[<Process(Process-8, stopped[1])>, <Process(Process-9, stopped[1])>, <Process(Process-10, stopped[1])>, <Process(Process-11, stopped[1])>, <Process(Process-12, stopped[1])>, <Process(Process-13, stopped[1])>, <Process(Process-14, stopped[1])>]
[False, False, False, False, False, False, False]

以此类推,直到最后一次尝试。

这意味着 a) 我的所有进程都在 0.5 秒内死亡 b) 同时,它们都没有成功执行函数 f,因为“打印”行从未执行(因此队列为空)。

【问题讨论】:

  • 控制台打印了什么?
  • @JeffUK 我编辑了我的帖子以详细说明。
  • 你是否有机会在 IDLE 中运行它?
  • related :*.com/questions/18204782/… 似乎在 IDLE 上,子进程没有机会将运行时错误打印到控制台,所以你什么都看不到,但在其他环境中你将根据此链接获得运行时错误。
  • 我正在使用 Spyder 运行我的代码。我解决了这个问题,请参阅下面我自己的答案,这是因为我的主要代码块没有被 if name == 'main' “保护”

标签: python multiprocessing


【解决方案1】:

我找到了我的代码没有按预期工作的原因:启动进程的代码块应该封装在if __name__ == '__main__':中。

documentation of multiprocessing module 说:

应该使用if __name__ == '__main__':保护程序的“入口点”

这样做,代码可以正常工作。

【讨论】:

  • Windows 上需要 if __name__== "__main__"` 保护。在 Linux、Mac Os 和其他系统上,多处理模块使用不会再次启动 Python 脚本的 fork 调用 - 也就是说为什么我的答案与此不同,我将重点关注此示例的其他问题。
【解决方案2】:

您的 f 函数不会让任何东西作为工作程序运行:一旦它运行最后一行,它就会结束并返回 - 这将关闭它所依赖的进程。

因此,预计您的最终检查中不会有任何进程处于活动状态。

令人惊讶的是,您说该值没有出现在调用者队列中 - 正如您发现的那样,这是因为在 Windows 中,与启动子进程调用本身相关的代码应该只在主进程上运行(这样做的方法是检查__name__ 变量是否等于"__main__") - 实际上,您很可能只是没有看到打印。我在这里运行了你的代码,它按预期工作。请注意,您只需在每个“尝试”中打印 一个 队列元素。由于每个进程只会将一个元素放入队列并退出,因此您应该会在前 15 次尝试中的 8 次后看到打印出的“hello”(前 4 次尝试将在子进程处于 2 秒内时发生) "sleep") ,并且在进程完成后没有打印。

如果你想让工作线程保持运行,你应该在你的目标函数中有一个while 循环,它将尝试从队列中读取消息并分派任务。这不是什么难写的东西,但是 Python 已经在 concurrent.futures 库中为您完成了这项工作:使用这种方法,是的,您创建了一个由 Python 运行时保持活动状态的子进程池,而您没有显式关闭它下来,您的每个目标函数都可以随时从主进程中单独调用,重用该进程 - 该函数只是一个普通的 Python 函数 - 无需循环,听队列等......

【讨论】:

  • 感谢 Windows/非 Windows 附加说明!事实上,我忘了提及我正在使用的操作系统。
最近更新 更多