用Python生成多个进程

4

我之前尝试使用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

3
去掉target=pri()中的括号。 - uspectaculum
2个回答

6
Process类需要一个可调用对象作为其目标参数。
不要在单独的进程中运行函数,而是调用它并将其结果(在本例中为None)传递给Process类。
只需更改以下内容:
mp.Process(target=pri())

使用:

mp.Process(target=pri)

这仍然返回相同的pid。它甚至不会打印到屏幕上,因为它在子进程中运行。 - Chen A.
我刚试了一下这段代码,我得到了三个不同的PID。根据操作系统的不同,它可能会重新使用PID,但在这种情况下,这对于OP来说并不相关,因为范围是测试“multiprocessing”库。 - noxdafox

0

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

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

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

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

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

这只适用于Windows操作系统。以下示例是在Windows平台上创建的。在类Unix操作系统上,不需要使用sleep。
更方便的解决方案如下:
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
>>> 

使用 sleep 函数:

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

你说得对。我刚在 Linux 机器上试了一下,确实打印出唯一的 PID。然而,除了打印之外,为了让进程能够与主线程通信,它必须使用通信接口(队列、套接字等)。使用“Pool”可以解决这个问题,从而简化了多进程处理。 - Chen A.
这取决于使用情况,有时您只需要像 HTTP 服务器一样旋转某些东西。在这些情况下,您不太关心函数返回的内容。进程适用于许多用例,我个人最喜欢的是它们为应用程序提供的隔离性。如果另一个进程中的逻辑崩溃,您的应用程序将保持不受影响。这无法像线程或协程那样轻松实现。 - noxdafox
@noxdafox 我想知道print语句在多进程中是如何工作的。它是如何在主控制台中打印的?我认为不同的进程不能像那样通信,必须通过某些手段,如队列、管道或IPC等进行通信。但是,为什么子进程的print语句可以在主线程中打印出来呢? - Chen A.
我不熟悉Windows内部机制,但在Unix上,“fork”会复制进程地址空间以及文件描述符。因此,新分叉的进程将具有与父进程相同的标准输出文件描述符。因此,您会看到输出指向同一控制台。 - noxdafox
快速浏览了一下代码。控制台通过专用管道传递给新生成的进程。这就是为什么在处理“spawn”启动策略时需要更多的可拾取性的原因。 - noxdafox
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接