Python多进程编程:实战方式入门
Wong Shouhao

概述

我们在Python异步编程实战入门:从概念到实战讨论了异步编程的相关方法,并指出异步编程适用于 IO 密集型应用的编写。在此文中,我们将使用multiprocessing实践 Python 中的多进程编程。与异步编程不同,多进程编程可以解决计算密集型应用,而且可以绕过 Python 运行中最令人沮丧的组件——GIL

多进程编程可以充分利用现代处理器多核的特性,将代码分布到每个CPU核中运行。与异步编程类似,多进程编程的 API 等也在不断变化,笔者使用 Python 版本为3.11,CPU为i7-10875H,操作系统为Windows 10

与上一篇文章类似,本文不会大篇幅的介绍相关概念,而侧重于通过代码示例帮助读者快速掌握多进程编程。

基础模式

在真正讨论多进程编程的相关概念及编程方法前,我们首先给出一个简单的例子:

1
2
3
4
5
6
7
8
9
10
11
12
from multiprocessing import Process
from time import sleep

def task():
sleep(1)
print("Hello world in task")

if __name__ == '__main__':
process = Process(target=task)
process.start()
print("Main task")
process.join()

该代码输出结果为:

1
2
Main task
Hello world in task

此代码较为简单,但展示了多进程编程的基础模式。我们定义一个函数,并使用Process(target=task)将其包装为一个进程,随后调用process.start()启用此进程,最后使用process.join()阻塞主进程等待task进程完成。

注意,在多进程编程中,必须使用if __name__ == '__main__':来标识代码的开始

在上文中,我们提及使用process.join阻塞主进程。对于部分读者而言,阻塞可能是一个新鲜概念。简单来说,阻塞意味着等待,即主进程等待task子进程完成任务。如下图:

MultiProcess

读者可以尝试去掉process.join(),会观察到输出结果不变。这是因为Python会隐式调用join方法,但我们并不推荐读者依赖于此隐式调用,这可能导致一些意外情况的发生。

在此处,我们需要强调在多进程编程中最重要的概念就是阻塞。可以认为多进程编程的核心就是设计阻塞

在此处,我们再给出一个需要使用参数的进程的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from multiprocessing import Process
from time import sleep

def sleep_print(s: int):
sleep(s)
print(f"{s} sleep")

if __name__ == '__main__':
process1 = Process(target=sleep_print, args=(1,))
process2 = Process(target=sleep_print, args=(2,))
process1.start()
process2.start()
print("Main task")
process1.join()
process2.join()

我们可以通过args输入函数所需要的参数。上述代码输出为:

1
2
3
Main task
1 sleep
2 sleep