Python多进程并行multiprocess基础

Python多进程并行multiprocess基础

  • [一、 简介](#一、 简介)
  • [二、 核心组件](#二、 核心组件)
    • [1、 Process类](#1、 Process类)
    • [2、 Pool](#2、 Pool)
      • [`Pool.map(func, iterable)`](#Pool.map(func, iterable))
      • [`Pool.starmap(func, iterable_of_args)`](#Pool.starmap(func, iterable_of_args))
      • [`Pool.apply(func, args=())`](#Pool.apply(func, args=()))
      • [`Pool.apply_async(func, args=(), callback=None)`](#Pool.apply_async(func, args=(), callback=None))
      • [`Pool.map_async(func, iterable, callback=None)`](#Pool.map_async(func, iterable, callback=None))
    • [3、 进程间通信的部分待补充](#3、 进程间通信的部分待补充)
  • 三、阻塞和异步的概念

一、 简介

python中多进程编程可以通过内联的模块multiprocess实现。multiprocess模块是用于并行处理任务的工具,通过创建多个独立的进程(process)可以避开Cpython的全局解释器锁(GIL),适合于CPU密集型任务。

全局解释器锁(GIL, Global Interpreter Lock)是 CPython(Python 最常用的解释器实现)中的一个互斥锁,它的作用是:在同一时间只允许一个线程执行 Python 字节码(Python 原生代码)

GIL使得在多线程不用担心python对象的内存管理问题,Python 内置的内存分配、引用计数、垃圾回收等操作都只会由一个线程操作,不会"同时"被多个线程修改。内存管理的过程是串行的、安全的,单GIL保护的是python对象的内存操作,并不保证业务逻辑线程安全。

项目 是否线程安全 GIL 是否保护
Python 对象的引用计数
Python 内置内存分配器
变量、自增 赋值逻辑
多线程逻辑操作同一 dict/list

二、 核心组件

1、 Process类

multiprocessing.Process 是multiprocessing 模块中用于创建并管理子进程的类。

基本用法如下

python 复制代码
from multiprocessing import Process

def worker(num):
	print(f"Worker {num} is running")
if __name__ == "__main__":
	p = Process(target=worker, arg(1, )) # 创建一个进程,目标函数worker,传入参数1
	p.start()	# 启动进程,进程开始运行worker函数
	p.join()	# 主进程等待子进程结束

创建Process对象

Process的构造参数

参数名 类型 说明 默认值
group None 保留参数,目前未使用,必须是None None
target callable 进程启动后调用的目标函数 None
name str 进程的名字,可以用来区分不同的进程,主要用于调试和日志 Process-N自动命名
args tuple 传给target函数的位置参数,元组形式 ()(空元组)
kwargs dict 传给target函数的关键字参数,以字典形式 {}(空字典)
daemon bool 是否将进程设置为守护进程(后台进程),守护进程会随着主进程退出自动结束 继承父进程值

创建多个进程

python 复制代码
from multiprocessing import Process

def worker(num):
    print(f"Worker {num} started")
    # 模拟工作
    import time
    if num == 2:
        time.sleep(4)
    else:
        time.sleep(2)
    print(f"Worker {num} finished")

if __name__ == "__main__":
    processes = []
    for i in range(4):
        p = Process(target=worker, args=(i,))
        p.start()
        processes.append(p)
    
    for p in processes:
        p.join()  # 等待所有子进程结束
    print("All workers finished")

输出结果:

python 复制代码
Worker 0 started
Worker 1 started
Worker 2 started
Worker 3 started
Worker 0 finished
Worker 1 finished
Worker 3 finished
Worker 2 finished
All workers finished

使用Process创建的子进程和父进程之间相互独立,子进程有自己的内存空间,父子进程之间不共享内存,需要通过进程间通信协议(IPC)机制如Queue、Pipe、Manager来传递数据。

涉及进程间通信部分的内容暂时还没有遇到,遇到了再补充吧


2、 Pool

Pool是multiprocess中的进程池对象,用于管理一组进程的工作,自动维护一定数量的进程,避免频繁创建和销毁进程带来的开销。

参数名 类型 作用
processes int 指定进程数
initializer callable 每个子进程启动时运行一次
initargs tuple initializer 的参数
maxtasksperchild int 限制每个进程最多执行的任务数
context 特殊对象 控制启动方式(spawn 等)

常用参数是进程数

Pool常用方法如下

Pool.map(func, iterable)

类似于map函数,并行执行func,但是func函数只能接收一个参数,其返回值为一个有序结果列表。

python 复制代码
from multiprocessing import Pool

def square(x):
    return x * x

if __name__ == "__main__":
    with Pool(4) as pool:
        results = pool.map(square, [1, 2, 3, 4, 5])
        print(results)  # [1, 4, 9, 16, 25]

Pool.starmap(func, iterable_of_args)

和map基本一样,但是func可以接收多个参数,参数以list[tuple]的形式传入。

python 复制代码
from multiprocessing import Pool

def power(base, exp):
    return base ** exp

if __name__ == "__main__":
    with Pool(3) as pool:
        results = pool.starmap(power, [(2, 3), (3, 2), (4, 1)])
        print(results)  # [8, 9, 4]

Pool.apply(func, args=())

一次调用只会使用一个进程,需要多次调用,不是真正的并行,同时会阻塞

python 复制代码
from multiprocessing import Pool

def add(x, y):
    return x + y

if __name__ == "__main__":
    with Pool(2) as pool:
        result = pool.apply(add, (3, 5))
        print(result)  # 8

Pool.apply_async(func, args=(), callback=None)

和apply不同的是apply_async是异步非阻塞的,**"异步非阻塞"**是指:任务会被并行执行,主线程不需要等待任务完成就可以继续往下运行。可以在稍后用 .get() 来获取结果,也可以设置回调函数 callback= 自动处理结果。

python 复制代码
from multiprocessing import Pool
import time

def slow_function(x):
    time.sleep(2)
    return x * 2

if __name__ == '__main__':
    with Pool(2) as pool:
        # 异步提交任务
        result1 = pool.apply_async(slow_function, args=(10,))
        result2 = pool.apply_async(slow_function, args=(20,))

        # 主进程继续执行,不会等待
        print("Tasks submitted... doing other things meanwhile...")

        # 等待并获取结果
        print("Result 1:", result1.get())
        print("Result 2:", result2.get())

程序会在 ~2 秒后输出两个结果,而不是 ~4 秒。

Pool.map_async(func, iterable, callback=None)

是map的异步非阻塞版。

python 复制代码
from multiprocessing import Pool

def square(x):
    return x * x

with Pool(4) as pool:
    result = pool.map_async(square, [1, 2, 3, 4])
    print("Tasks submitted.")
    
    # 等待最多5秒后拿结果
    print("Results:", result.get(timeout=5))  # [1, 4, 9, 16]

3、 进程间通信的部分待补充

未完待续...


三、阻塞和异步的概念

阻塞和异步是两个经常一起出现的词

首先明确一些阻塞和异步的概念

异步:是指任务调度的方式,调用任务者发起一个任务后,不等待任务完成,接着执行后面的代码,任务在将来某个时间点完成,结果通过回调函数、事件通知、等待对象获得。

非阻塞:是发起一项操作,不用等待操作完成就能继续做别的事,常见于IO,更侧重于操作

几种方法的对比

方法 是否阻塞 是否可多参数 返回值类型 回调支持
map() ✅ 是 ❌ 否 结果列表 ❌ 无
starmap() ✅ 是 ✅ 是 结果列表 ❌ 无
apply() ✅ 是 ✅ 是 单个结果 ❌ 无
apply_async() ❌ 否 ✅ 是 AsyncResult ✅ 支持
map_async() ❌ 否 ❌ 否 AsyncResult ✅ 支持
相关推荐
张较瘦_28 分钟前
[论文阅读] 人工智能 + 软件工程 | 增强RESTful API测试:针对MongoDB的搜索式模糊测试新方法
论文阅读·人工智能·软件工程
深海潜水员1 小时前
【Python】 切割图集的小脚本
开发语言·python
Wendy14411 小时前
【边缘填充】——图像预处理(OpenCV)
人工智能·opencv·计算机视觉
27669582921 小时前
东方航空 m端 wasm req res分析
java·python·node·wasm·东方航空·东航·东方航空m端
钱彬 (Qian Bin)1 小时前
《使用Qt Quick从零构建AI螺丝瑕疵检测系统》——8. AI赋能(下):在Qt中部署YOLOv8模型
人工智能·qt·yolo·qml·qt quick·工业质检·螺丝瑕疵检测
Yolo566Q2 小时前
R语言与作物模型(以DSSAT模型为例)融合应用高级实战技术
开发语言·经验分享·r语言
Felven2 小时前
C. Challenging Cliffs
c语言·开发语言
星月昭铭2 小时前
Spring AI调用Embedding模型返回HTTP 400:Invalid HTTP request received分析处理
人工智能·spring boot·python·spring·ai·embedding
Dreamsi_zh2 小时前
Python爬虫02_Requests实战网页采集器
开发语言·爬虫·python
大千AI助手3 小时前
直接偏好优化(DPO):原理、演进与大模型对齐新范式
人工智能·神经网络·算法·机器学习·dpo·大模型对齐·直接偏好优化