Python 多进程编程

Python 多进程编程

多线程编程的复杂之处在于,要仔细地使用锁,来保证数据的完整。多线程的意义在于,可以在 I/O 相关的任务中,取得好的性能。在其它场景中,常见的应用程序没有重度的 I/O,多线程不是唯一的选择,还有其它并发或并行方案可以使用。这种方案是多进程方案。在 Python 中使用多进程可以避免 Python 的全局解释锁(GIL,Global Interpreter Lock)的限制,可以实现并行执行(高层看起来是并行,底层依然是并发)。如果应用程序是在多核CPU上运行,多进程编程尤其有益。在现实中,多进程编程,是 Python 可以充分利用多核 CPU 的唯一手段。

在 CPU 的多个进程之间,本质上默认没有共享内存。这意味着数据破坏的机率非常小。如果两个进程有共享数据,他们需要使用一些进程间通信机制。Python 支持进程间通信。

接下来,我们首先讲解一下创建进程的基本知识,后续会讲解进程间通信。

创建进程

Python 提供了 multiprocessing 包来支持多进程编程,它和多线程编程包非常像。multiprocessing 包包含了两个实现多进程的方案,一个是使用 Process对象,一个是使用 Pool 对象。接下来,我们逐一讨论这两个方案。

使用 Process 对象

通过创建一个 Process 对象,可以生成一个进程,接下来,可以使用 start 方法来启动它。下面是其示例代码:

import os
from multiprocessing import Process, current_process as cp
from time import sleep

def print_hello():
    sleep(2)
    print("{} - {}: 你有房有车有女/男朋友吗?".format(os.getpid(), cp().name))


def print_message(msg):
    sleep(1)
    print("{} - {}: {}".format(os.getpid(), cp().name, msg))

def main():
    processes = []

    # 创建进程
    processes.append(Process(target=print_hello, name = "进程 1"))
    processes.append(Process(target=print_hello, name = "进程 2"))
    processes.append(Process(target=print_message, args=["早上好"], name = "进程 3"))

    # 启动进程
    for p in processes:
        p.start()

    # 等待所有进程运行完毕
    for p in processes:
        p.join()

    print("退出主进程。")

if __name__ == '__main__':
    main()

上面代码的运行结果是(不一样的电脑会有不一样的运行结果):

6612 - 进程 3: 早上好
6340 - 进程 1: 你有房有车有女/男朋友吗?
11204 - 进程 2: 你有房有车有女/男朋友吗?
退出主进程。

可见,创建进程的代码,是简单明了的。

使用 Pool 对象

Pool 对象提供了一种方便的方式(使用它的 map 方法)创建进程、给每个进程设置一个函数,跨进程分发输入参数。在下面的示例代码中,我们提供了一个大小是 3 的进程池,然而提供了可供 5 个进程使用的参数。把进程池大小设置为 3 是为了确保在同一时间最大有 3 个进程是活动的,不管我们给 Pool 对象的 map 方法传递了多少形参。多余的形参将会在某个子进程处理完它当前的工作后,被传递这个子进程。下面是示例代码:

import os
from multiprocessing import Process, Pool, current_process as cp
from time import sleep

def print_message(msg):
    sleep(1)
    print("{} - {}: {}".format(os.getpid(), cp().name, msg))

def main():
    # 在进程池中创建进程
    with Pool(3) as pool:
        pool.map(print_message, ["提子", "葡萄", "苹果", "桔子", "梨"])

    print("退出主进程。")

if __name__ == '__main__':
    main()

上面代码的输出结果是:

8124 - SpawnPoolWorker-2: 提子
13556 - SpawnPoolWorker-1: 苹果
13384 - SpawnPoolWorker-3: 葡萄
8124 - SpawnPoolWorker-2: 桔子
13556 - SpawnPoolWorker-1: 梨

将输入参数分配给与一组池进程绑定的函数的神奇之处在于map方法。map方法等待所有函数完成执行,这就是为什么如果使用Pool 对象创建进程,则不需要使用join方法。

ProecessPool 的区别如下:

使用 Pool 对象使用 Porcess 对象
只有活动进程被保留在内存里所有创建成功能的进程都在内存里
更适合大型数据集和重复性任务对小数据集更友好
进程会阻塞到 I/O 操作上,直到获得 I/O资源进程不会被阻塞到 I/O 操作上

<完>

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dowhileprogramming

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值