深入理解Python多进程编程 multiprocessing

深入理解Python多进程编程 multiprocessing

flyfish

Python 的 multiprocessing 模块允许创建多个进程,从而可以利用多核处理器的能力来并行执行任务。这意味着程序的不同部分可以在不同的CPU核心上同时运行,极大地提高了处理效率,特别是在执行计算密集型任务时。

与多线程相比,multiprocessing 使用的是系统级的进程而不是线程。每个进程都有独立的内存空间和系统资源,而线程则共享同一个进程的内存空间。因此,在Python中(特别是由于全局解释器锁GIL的存在),对于CPU密集型任务,使用multiprocessing比多线程能更有效地利用多核CPU的优势。

进程的概念

在计算机操作系统中,进程是操作系统进行资源分配和调度的基本单位。一个进程可以包含多个线程。当使用multiprocessing模块时,可以创建新的进程,这些新进程将与主程序并行运行,并且它们各自拥有独立的内存空间。

示例代码1:单个进程打印数字

下面是一个简单的示例,演示如何使用multiprocessing模块创建一个进程来打印从1到5的数字:

python 复制代码
import multiprocessing
import time

def print_numbers():
    """打印从1到5的数字"""
    for i in range(1, 6):
        print("数字:", i)
        time.sleep(1)  # 模拟耗时操作

if __name__ == "__main__":
    # 创建一个新的进程
    process = multiprocessing.Process(target=print_numbers)

    # 启动进程
    process.start()

    # 等待进程完成
    process.join()
数字: 1
数字: 2
数字: 3
数字: 4
数字: 5
  • multiprocessing.Process():创建一个新的进程对象。
  • target=print_numbers:指定该进程的目标函数为print_numbers
  • process.start():启动进程。
  • process.join():等待进程结束。

示例代码2:两个进程分别打印不同字符串

下面是另一个示例,演示如何同时启动两个进程,每个进程打印不同的字符串:

python 复制代码
import multiprocessing

def print_message(message):
    """打印传入的消息"""
    print(f"消息: {message}")

if __name__ == "__main__":
    # 创建两个进程
    process1 = multiprocessing.Process(target=print_message, args=("Hello from Process 1",))
    process2 = multiprocessing.Process(target=print_message, args=("Hello from Process 2",))

    # 启动两个进程
    process1.start()
    process2.start()

    # 等待两个进程都完成
    process1.join()
    process2.join()
消息: Hello from Process 1
消息: Hello from Process 2

在这个例子中,定义了一个print_message函数,它接受一个字符串参数并打印出来。然后,创建了两个进程,每个进程都调用这个函数,但传递了不同的字符串参数。通过args参数,可以向目标函数传递额外的参数。最后,启动这两个进程,并等待它们完成各自的执行。这样,就可以看到两个进程几乎同时开始工作,并打印出各自的消息。

示例3:使用 multiprocessing.Value 在多个进程中共享一个计数器

multiprocessing.Value
Value 允许多个进程共享一个值。它适用于需要在多个进程中共享简单数据类型(如整数或浮点数)的情况。

python 复制代码
import multiprocessing

def increment(counter, lock):
    """增加计数器的值"""
    for _ in range(1000):
        with lock:
            counter.value += 1

if __name__ == "__main__":
    # 创建一个共享的整数值和锁
    counter = multiprocessing.Value('i', 0)  # 'i' 表示整数类型
    lock = multiprocessing.Lock()

    # 创建多个进程来增加计数器
    processes = [multiprocessing.Process(target=increment, args=(counter, lock)) for _ in range(10)]

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

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

    print("最终计数器值:", counter.value)
最终计数器值: 10000
  • multiprocessing.Value(typecode_or_type, *args, lock=True) :创建一个新的共享值对象。typecode_or_type 指定了要共享的数据类型(例如 'i' 表示整数)。
  • value.value:访问共享值的实际内容。
  • lock:确保对共享资源的安全访问,防止竞态条件。

进程(Process)和线程(Thread)在Python中的区别

特性 进程(Process) 线程(Thread)
内存空间 每个进程有独立的内存空间 所有线程共享同一进程的内存空间
资源消耗 开销较大,需要更多系统资源 轻量级,开销小,资源共享
通信难度 进程间通信复杂(IPC),如管道、套接字等 线程间通信简单,直接访问相同变量和数据结构
全局解释器锁(GIL) 不受GIL限制,适合计算密集型任务 受GIL限制,对于计算密集型任务效率提升有限
适用场景 计算密集型任务,稳定性要求高的应用 I/O密集型任务,快速响应用户界面的应用
崩溃影响 一个进程崩溃不影响其他进程 一个线程出错可能导致整个进程崩溃

Python中多线程(Thread)和多进程(Process)的区别

特性 多线程(Thread) 多进程(Process)
内存空间 所有线程共享同一进程的内存空间 每个进程有独立的内存空间
资源消耗 轻量级,开销小,资源共享 开销较大,需要更多系统资源
通信难度 线程间通信简单,直接访问相同变量和数据结构 进程间通信复杂(IPC),如管道、套接字等
全局解释器锁 (GIL) 受GIL限制,对于计算密集型任务效率提升有限 不受GIL限制,适合计算密集型任务
适用场景 I/O密集型任务,快速响应用户界面的应用 计算密集型任务,稳定性要求高的应用
崩溃影响 一个线程出错可能导致整个进程崩溃 一个进程崩溃不影响其他进程
创建与销毁开销 创建和销毁开销较小 创建和销毁开销较大
并发性能 对于I/O密集型任务性能较好,但对于CPU密集型任务受限 对于CPU密集型任务性能较好
示例用途 网络请求、文件读写、GUI应用等 数据分析、图像处理、科学计算等

进程间通信

在Python的multiprocessing模块中,提供了几种常用的进程间通信(IPC)方式,包括队列(Queue)、管道(Pipe)等。这些工具允许不同的进程之间安全地传递数据。

使用 multiprocessing.Queue 实现进程间通信

Queue 是一个线程和进程安全的 FIFO 队列,非常适合用于进程间的简单数据交换。

示例代码:

python 复制代码
import multiprocessing

def producer(queue):
    """生产者函数,向队列中添加数据"""
    for i in range(5):
        queue.put(f"数据 {i}")
        print(f"生产者放入: 数据 {i}")

def consumer(queue):
    """消费者函数,从队列中取出数据"""
    while not queue.empty():
        data = queue.get()
        print(f"消费者获取: {data}")

if __name__ == "__main__":
    # 创建一个队列对象
    queue = multiprocessing.Queue()

    # 创建生产者和消费者进程
    p1 = multiprocessing.Process(target=producer, args=(queue,))
    p2 = multiprocessing.Process(target=consumer, args=(queue,))

    # 启动进程
    p1.start()
    p2.start()

    # 等待两个进程完成
    p1.join()
    p2.join()
生产者放入: 数据 0
生产者放入: 数据 1
生产者放入: 数据 2
生产者放入: 数据 3
生产者放入: 数据 4
消费者获取: 数据 0
消费者获取: 数据 1
消费者获取: 数据 2
消费者获取: 数据 3
消费者获取: 数据 4
  • 队列的使用queue.put() 用于向队列中添加数据,queue.get() 用于从队列中取出数据。
  • 数据传递原理 :生产者进程通过调用 put 方法将数据放入队列,而消费者进程通过调用 get 方法从队列中取出数据。Queue 对象是进程安全的,因此多个进程可以同时访问它而不发生冲突。
使用 multiprocessing.Pipe 实现进程间通信

Pipe 提供了一个双向通道,适用于两个进程之间的直接通信。

示例代码:

python 复制代码
import multiprocessing

def sender(conn, messages):
    """发送者函数,通过管道发送消息"""
    for msg in messages:
        conn.send(msg)
        print(f"发送者发送: {msg}")
    conn.close()

def receiver(conn):
    """接收者函数,通过管道接收消息"""
    while True:
        msg = conn.recv()
        if msg == "END":
            break
        print(f"接收者接收: {msg}")

if __name__ == "__main__":
    # 创建一个管道对象
    parent_conn, child_conn = multiprocessing.Pipe()

    # 准备要发送的消息
    messages = ["Hello", "from", "sender", "END"]

    # 创建发送者和接收者进程
    p1 = multiprocessing.Process(target=sender, args=(child_conn, messages))
    p2 = multiprocessing.Process(target=receiver, args=(parent_conn,))

    # 启动进程
    p1.start()
    p2.start()

    # 等待两个进程完成
    p1.join()
    p2.join()
发送者发送: Hello
发送者发送: from
发送者发送: sender
发送者发送: END
接收者接收: Hello
接收者接收: from
接收者接收: sender

进程池的使用

multiprocessing.Pool 是一个用于管理一组工作进程的类,它可以简化并行任务的分配和结果收集。

示例代码:使用 Pool 并行计算数字的平方
python 复制代码
import multiprocessing

def square(n):
    """计算一个数的平方"""
    return n * n

if __name__ == "__main__":
    # 定义要处理的数字列表
    numbers = [1, 2, 3, 4, 5]

    # 创建一个包含4个进程的进程池
    with multiprocessing.Pool(processes=4) as pool:
        # 使用map方法将square函数应用于每个数字
        results = pool.map(square, numbers)

    print("结果:", results)
结果: [1, 4, 9, 16, 25]
  • 进程池的概念和作用Pool 允许你指定一定数量的工作进程,并且可以通过 mapapply 等方法轻松地将任务分配给这些进程。这样可以有效地利用多核CPU来加速计算密集型任务。
  • 设置进程池大小 :通过 processes 参数指定进程池中的工作进程数量,默认情况下,它会根据系统CPU核心数自动调整。
  • 处理任务的方式pool.map() 方法类似于内置的 map() 函数,但它会在多个进程中并行执行。在这个例子中,我们将 square 函数应用到 numbers 列表中的每个元素,并返回计算结果。

Semaphore(信号量)

信号量是一种更高级的同步机制,可以用来控制同时访问某一资源的进程数量。

示例:使用 Semaphore 控制并发访问

python 复制代码
import multiprocessing
import time

def worker(semaphore, name):
    with semaphore:
        print(f"{name} 获得信号量")
        time.sleep(1)

if __name__ == "__main__":
    semaphore = multiprocessing.Semaphore(3)  # 最多允许3个进程同时访问

    processes = [multiprocessing.Process(target=worker, args=(semaphore, f"进程 {i}")) for i in range(6)]

    for p in processes:
        p.start()

    for p in processes:
        p.join()

Event(事件)

事件是一种简单的线程间通信机制,可以让一个或多个进程等待某个特定事件的发生。

示例:使用 Event 实现进程间的同步

python 复制代码
import multiprocessing
import time

def wait_for_event(event):
    print("等待事件触发...")
    event.wait()  # 阻塞直到事件被设置
    print("事件已触发!")

def set_event(event):
    time.sleep(3)
    event.set()  # 触发事件

if __name__ == "__main__":
    event = multiprocessing.Event()

    p1 = multiprocessing.Process(target=wait_for_event, args=(event,))
    p2 = multiprocessing.Process(target=set_event, args=(event,))

    p1.start()
    p2.start()

    p1.join()
    p2.join()

Manager(管理器)

Manager 提供了更高层次的接口,可以创建可以在不同进程之间共享的数据结构,如列表、字典等。

示例:使用 Manager 创建共享数据结构

python 复制代码
import multiprocessing

def append_to_list(shared_list, item):
    shared_list.append(item)
    print(f"添加到共享列表: {item}")

if __name__ == "__main__":
    with multiprocessing.Manager() as manager:
        shared_list = manager.list()  # 创建一个可共享的列表

        processes = [multiprocessing.Process(target=append_to_list, args=(shared_list, i)) for i in range(5)]

        for p in processes:
            p.start()

        for p in processes:
            p.join()

        print("最终共享列表:", list(shared_list))

文中processes = [multiprocessing.Process(target=append_to_list, args=(shared_list, i)) for i in range(5)]这一句 等于下面的代码

py 复制代码
processes = []
for i in range(5):
    p = multiprocessing.Process(target=append_to_list, args=(shared_list, i))
    processes.append(p)

共享内存

multiprocessing 还支持通过共享内存的方式在进程之间共享数据,这对于大规模数据共享特别有用。

示例:使用 Array 共享数组

python 复制代码
import multiprocessing

def modify_array(shared_array, index, value):
    shared_array[index] = value

if __name__ == "__main__":
    array = multiprocessing.Array('i', [1, 2, 3, 4, 5])  # 创建共享数组

    processes = [
        multiprocessing.Process(target=modify_array, args=(array, i, i*10)) for i in range(len(array))
    ]

    for p in processes:
        p.start()

    for p in processes:
        p.join()

    print("修改后的数组:", list(array))
修改后的数组: [0, 10, 20, 30, 40]
相关推荐
奔跑吧邓邓子5 分钟前
【Python爬虫(12)】正则表达式:Python爬虫的进阶利刃
爬虫·python·正则表达式·进阶·高级
码界筑梦坊29 分钟前
基于Flask的京东商品信息可视化分析系统的设计与实现
大数据·python·信息可视化·flask·毕业设计
pianmian130 分钟前
python绘图之箱型图
python·信息可视化·数据分析
csbDD1 小时前
2025年网络安全(黑客技术)三个月自学手册
linux·网络·python·安全·web安全
赔罪2 小时前
Python 高级特性-切片
开发语言·python
伊一大数据&人工智能学习日志3 小时前
selenium爬取苏宁易购平台某产品的评论
爬虫·python·selenium·测试工具·网络爬虫
说是用户昵称已存在3 小时前
Pycharm+CodeGPT+Ollama+Deepseek
ide·python·ai·pycharm
Fansv5873 小时前
深度学习-2.机械学习基础
人工智能·经验分享·python·深度学习·算法·机器学习
wang_yb4 小时前
『Python底层原理』--Python对象系统探秘
python·databook
databook4 小时前
『Python底层原理』--Python对象系统探秘
后端·python