【问题标题】:Spawning multiple processes with Python使用 Python 生成多个进程
【发布时间】:2017-10-15 13:39:15
【问题描述】:

之前我尝试在 python 中使用 threading 模块来创建多个线程。然后我了解了GIL 以及它如何不允许在单台机器上利用多个 CPU 内核。所以现在我正在尝试进行多处理(我并不严格需要单独的线程)。

这是我编写的示例代码,用于查看是否正在创建不同的进程。但从下面的输出中可以看出,我每次都得到相同的进程 ID。所以没有创建多个进程。我错过了什么?

import multiprocessing as mp
import os

def pri():
    print(os.getpid())

if __name__=='__main__':

    # Checking number of CPU cores
    print(mp.cpu_count())

    processes=[mp.Process(target=pri()) for x in range(1,4)]

    for p in processes:
        p.start()

    for p in processes:
        p.join()

输出:

4
12554
12554
12554

【问题讨论】:

  • 删除target=pri()上的()

标签: python multiprocessing python-multiprocessing


【解决方案1】:

Process 类需要一个可调用对象作为其目标。

您不是在单独的进程中运行该函数,而是调用它并将其结果(在本例中为None)传递给Process 类。

只需更改以下内容:

mp.Process(target=pri())

与:

mp.Process(target=pri)

【讨论】:

  • 这仍然返回相同的 pid。它甚至不会将其打印到屏幕上,因为它在子进程中运行。
  • 我刚刚尝试了代码,我得到了 3 个不同的 PID。根据操作系统,它可能会重新使用 PID,在这种情况下,它与 OP 无关,因为范围是测试 multiprocessing 库。
【解决方案2】:

由于子进程在不同的进程上运行,您不会看到它们的打印语句。它们也不共享相同的内存空间。您将 pri() 传递给需要 pri 的目标。您需要传递一个 可调用对象,而不是执行它。

您看到的打印是您的主线程执行的一部分。因为你传递了pri(),所以实际上执行了代码。您需要更改您的代码,以便 pri 函数 返回 值,而不是打印它。

然后你需要实现一个queue,你的所有线程写入到它,当它们完成时,你的主线程读取队列。

multiprocessing 模块的一个很好的特性是Pool 对象。它允许您创建一个线程池,然后就可以使用它。更方便。

我试过你的代码,问题是命令执行太快,所以操作系统重用了 PID。如果您在 pri 函数中添加 time.sleep(1),它将按预期工作。

这仅适用于 Windows。下面的例子是在 Windows 平台上制作的。在类 Unix 的机器上,你不需要睡眠。

比较方便的方案是这样的:

from multiprocessing import Pool
from time import sleep
import os

def pri(x):
    sleep(1)
    return os.getpid()

def use_procs():
    p_pool = Pool(4)
    p_results = p_pool.map(pri, [_ for _ in range(1,4)])
    p_pool.close()
    p_pool.join()
    return p_results

if __name__ == '__main__':
    res = use_procs()
    for r in res:
        print r

不睡觉:

==================== RESTART: C:/Python27/tests/test2.py ====================
6576
6576
6576
>>> 

与睡眠:

==================== RESTART: C:/Python27/tests/test2.py ====================
10396
10944
9000

【讨论】:

  • 如果您使用Pool,任务将由第一个可用的工作人员处理。由于 Windows 使用 spawn 启动方法,进程的启动速度比 Unix 慢(默认使用 fork)。因此,第一个工作进程在其他任务准备好之前“窃取”了所有任务。这就是为什么您只会在逻辑中看到第一个 PID。
  • 你是对的。我刚刚在 Linux 机器上尝试过,它确实打印了唯一的 PID。然而,除了打印之外,为了让进程与主线程通信,它必须使用通信接口(队列、套接字等)。使用Pool 可以解决这个问题,从而简化多处理。
  • 这取决于用例,有时您只需要旋转诸如 HTTP 服务器之类的东西。在这些情况下,您不太关心函数返回什么。流程适用于许多用例,我个人最喜欢的是它们为您的应用程序提供的隔离。如果另一个进程中的逻辑崩溃,您的应用程序将不受影响。这无法通过线程或协程轻松实现。
  • @noxdafox 我想知道打印语句如何与多进程一起使用。它是如何在主控制台中打印的?我认为不同的进程不能这样通信,它必须通过一些方式,如队列、管道或 ipc 等。主线程中打印的子进程的打印语句怎么来的?跨度>
  • 我不熟悉 Windows 内部结构,但在 Unix 上,fork 复制了进程地址空间以及文件描述符。因此,新分叉的进程将具有与父进程相同的标准输出文件描述符。因此,您会看到输出指向同一个控制台。
猜你喜欢
  • 1970-01-01
  • 2021-02-22
  • 1970-01-01
  • 2012-07-21
  • 2021-05-14
  • 2011-01-02
  • 1970-01-01
  • 2013-01-02
  • 2018-09-03
相关资源
最近更新 更多