Python 多进程(Process)基础

1️⃣ 程序、进程与线程的基本概念

名称 概念
程序(Program) 一组指令的有序集合(如.py文件),是静态的
进程(Process) 程序运行后在内存中分配资源的实例,包含代码、数据和系统资源,是动态的

🔸 结论:

  • 程序 → 启动后变成 → 进程

2️⃣ 创建子进程的方式:multiprocessing.Process

from multiprocessing import Process

📌 基本语法:

p = Process(group=None, target=函数名, name='进程名', args=(位置参数元组,), kwargs={关键字参数字典})

📌 参数说明:

参数 含义
group 分组功能(基本不用,默认 None
target 子进程要执行的函数
name 子进程名称,默认是 Process-N
args 传给函数的位置参数(元组)
kwargs 传给函数的关键字参数(字典)

3️⃣ 常用方法和属性

方法/属性 说明
start() 启动子进程
join(timeout) 主进程等待子进程执行结束(可设置最大等待时间)
is_alive() 检查子进程是否还在运行
terminate() 强制终止子进程
pid 获取子进程的 PID
name 获取进程名称
run() 若没有指定 target,默认执行 run 方法

4️⃣ 基本示例:创建并运行子进程

from multiprocessing import Process
import os, time

def task(name):
    print(f'子进程运行中:{name}, PID={os.getpid()}, 父PID={os.getppid()}')
    time.sleep(1)

if __name__ == '__main__':
    print("主进程开始")

    for i in range(3):
        p = Process(target=task, args=(f"任务{i+1}",))
        p.start()
        print(f"{p.name} 是否还活着?", p.is_alive())
        p.join()  # 阻塞主进程,等子进程结束
        print(f"{p.name} 结束后是否还活着?", p.is_alive())

    print("主进程结束")
---
# 示例输出:

  主进程开始
  Process-1 是否还活着? True
  子进程运行中:任务1, PID=25904, 父PID=8636
  Process-1 结束后是否还活着? False
  Process-2 是否还活着? True
  子进程运行中:任务2, PID=13788, 父PID=8636
  Process-2 结束后是否还活着? False
  Process-3 是否还活着? True
  子进程运行中:任务3, PID=24928, 父PID=8636
  Process-3 结束后是否还活着? False
  主进程结束

5️⃣ 不传 target 的情况下 run() 的行为

from multiprocessing import Process

p = Process()  # 未传 target
p.start()      # 只会调用 Process 内部默认的 run(),不会执行任务

🧠 说明:

  • 如果不传 targetstart() 只会调用默认的 run() 方法,不执行任何具体函数代码

6️⃣ 自定义类并重写 run() 方法

适用于更复杂的子进程管理,推荐使用。

from multiprocessing import Process
import os

class MyProcess(Process):
    def __init__(self, name):
        super().__init__()
        self.name = name

    def run(self):
        print(f'子进程:{self.name}, PID={os.getpid()}, 父PID={os.getppid()}')

if __name__ == '__main__':
    print("主进程启动")
    for i in range(1, 4):
        p = MyProcess(name=f"自定义进程-{i}")
        p.start()
        p.join()
    print("主进程结束")

---
#示例输出:

  主进程启动
  子进程:自定义进程-1, PID=21540, 父PID=1612
  子进程:自定义进程-2, PID=31500, 父PID=1612
  子进程:自定义进程-3, PID=6528, 父PID=1612
  主进程结束

7️⃣ terminate() 方法 —— 强制终止进程

from multiprocessing import Process
import os, time

def task(name):
    print(f"子进程运行中:{name}, PID={os.getpid()}")
    time.sleep(3)

if __name__ == '__main__':
    p = Process(target=task, args=("被终止的任务",))
    p.start()
    time.sleep(1)  # 给子进程1秒时间运行
    p.terminate()  # 强制终止
    print(f"{p.name} 已被终止")

⚠️ 注意:

  • terminate() 是强制终止,不能保证资源完全释放,慎用!
  • 常见用在进程卡死或死循环时

8️⃣ 主进程等待所有子进程结束(使用 join)

  • 主进程和子进程是并发运行的,主进程不会自动等待子进程完成。

  • 如果希望主进程等待子进程完成,需要在启动子进程后调用 .join()

from multiprocessing import Process
import os, time

def task():
    print(f"子进程PID={os.getpid()}, 父PID={os.getppid()}")
    time.sleep(1)

if __name__ == '__main__':
    print("主进程启动")
    process_list = []
    for _ in range(5):
        p = Process(target=task)
        p.start()
        process_list.append(p)

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

    print("主进程结束")

✅ 进程小结

项目 说明
启动进程 p.start()
等待进程完成 p.join()
判断进程是否活着 p.is_alive()
强制结束进程 p.terminate()
创建进程的方式 1. 传 target 运行函数
2. 重写 run 方法
主子进程关系 主进程不会自动等待子进程,需显式 join

9️⃣ 进程池:multiprocessing.Pool

适合大量任务,避免创建过多进程导致资源浪费。

✅ 基本用法-非阻塞的方式

# 创建进程
from multiprocessing import Pool

import os,time

# 编写任务
def task(name):
    print(f'子进程的pid:{os.getpid()},执行任务:{name}')
    time.sleep(1)

if __name__ == '__main__':
    # 主进程
    start=time.time()
    print('父进程开始执行')
    # 创建进程池
    p=Pool(3)
    # 创建任务
    for i in range(10):
        # 以非阻塞的方式
        p.apply_async(func=task,args=(i,))
    p.close() # 关闭进程池不在接收新任务
    p.join()  # 阻塞父进程,等待所有的子进程执行完毕之后,才会执行父进程中的代码
    print('所有的子进程执行完毕,父进程执行结束')
    print(time.time()-start)
----
示例输出:
  父进程开始执行
  子进程的pid:9908,执行任务:0
  子进程的pid:15876,执行任务:1
  子进程的pid:12668,执行任务:2
  子进程的pid:9908,执行任务:3
  子进程的pid:15876,执行任务:4
  子进程的pid:12668,执行任务:5
  子进程的pid:9908,执行任务:6
  子进程的pid:15876,执行任务:7
  子进程的pid:12668,执行任务:8
  子进程的pid:9908,执行任务:9
  所有的子进程执行完毕,父进程执行结束
  4.1847922801971436

✅ 基本用法-阻塞的方式

# 创建进程
from multiprocessing import Pool

import os,time

# 编写任务
def task(name):
    print(f'子进程的pid:{os.getpid()},执行任务:{name}')
    time.sleep(1)

if __name__ == '__main__':
    # 主进程
    start=time.time()
    print('父进程开始执行')
    # 创建进程池
    p=Pool(3)
    # 创建任务
    for i in range(10):
        # 以阻塞的方式
        p.apply(func=task,args=(i,))
    p.close() # 关闭进程池不在接收新任务
    p.join()  # 阻塞父进程,等待所有的子进程执行完毕之后,才会执行父进程中的代码
    print('所有的子进程执行完毕,父进程执行结束')
    print(time.time()-start)
-----
示例输出:
  父进程开始执行
  子进程的pid:4484,执行任务:0
  子进程的pid:27940,执行任务:1
  子进程的pid:31964,执行任务:2
  子进程的pid:4484,执行任务:3
  子进程的pid:27940,执行任务:4
  子进程的pid:31964,执行任务:5
  子进程的pid:4484,执行任务:6
  子进程的pid:27940,执行任务:7
  子进程的pid:31964,执行任务:8
  子进程的pid:4484,执行任务:9
  所有的子进程执行完毕,父进程执行结束
  10.161132097244263

✅ 方法说明

方法名 说明
apply_async() 非阻塞提交任务(推荐)
apply() 阻塞提交(等待上一个任务完成才继续)
close() 关闭进程池,不能再添加任务
join() 等待子进程完成(要放在 close() 后面)
terminate() 立即终止所有进程(会中断任务)

🔟并发 vs 并行(易混概念)

概念 说明
并发 多任务交替执行逻辑上同时进行(如一个 CPU 快速切换任务)
并行 多任务真正同时执行(如多核 CPU 各自处理一个任务)

📌 举例:

  • 并发:你一边做饭,一边接电话,但不是同时操作

  • 并行:你做饭,朋友接电话,同时进行

posted @ 2025-05-05 16:07  kyle_7Qc  阅读(86)  评论(0)    收藏  举报