进程简介

进程是操作系统进行资源分配和调度的基本单位,是操作系统的基础。一个正在运行的程序或软件就是一个进程。一个进程中可以创建多个线程或多进程来完成多任务。

进程状态

  • 就绪状态:运行的所有条件都已满足,等待CPU执行。
  • 执行状态:CPU正在执行其功能。
  • 等待(阻塞)状态:等待某些条件满足,例如 sleep() 函数也属于等待状态。

使用 multiprocessing 模块管理进程

Python 的 multiprocessing 模块提供了 Process 类来代表进程对象,允许我们轻松地创建子进程,并在多个 CPU 核心上并行执行任务。

创建与启动进程

from multiprocessing import Process, current_process
import os

def process_test():
    print(f"当前父进程PID: {os.getppid()}")
    print(f"当前进程PID: {os.getpid()}")
    print(f"当前进程名: {current_process().name}")

if __name__ == '__main__':
    p1 = Process(target=process_test, name="进程1")
    p2 = Process(target=process_test, name="进程2")

    print(f"主进程PID: {os.getpid()}")
    print(f"主进程的父进程PID: {os.getppid()}")

    p1.start()
    print(f"进程: {p1.name} 运行状态: {p1.is_alive()}")  # 打印 True
    p2.start()
    p1.join()
    print(f"进程: {p1.name} 运行状态: {p1.is_alive()}")  # 打印 False
    p2.join()

    print("p1:", p1.name, "p2:", p2.name)

方法与属性

  • start():开启子进程。
  • is_alive():判断子进程是否还存活。
  • join():等待子进程执行结束。
  • name:当前进程别名,默认为 'Process-X'。
  • pid:当前进程编号。

进程间通信

由于进程之间不共享全局变量,因此需要使用特定的方法进行数据交换。最常用的方式是通过 Queue 队列。

使用 Queue 实现进程间通信

from multiprocessing import Process, Queue
import time

def write_data(queue):
    for i in range(5):
        queue.put(i)
        print(f"{i} 被存入队列")
        time.sleep(0.2)

def read_data(queue):
    while not queue.empty():
        print(f"取出的数据: {queue.get()}")

if __name__ == '__main__':
    q = Queue()
    pw = Process(target=write_data, args=(q,))
    pr = Process(target=read_data, args=(q,))

    pw.start()
    pw.join()
    pr.start()
    pr.join()

注意事项

  • 如果尝试向已满的队列中添加数据或从空队列中取数据而没有适当的超时或检查机制,程序将会被阻塞。
  • 应该避免在生产环境中直接使用 .put().get() 而没有相应的异常处理或超时设置。

补充说明

Windows 中 if __name__ == '__main__': 的重要性

原理解释
在 Unix-like 系统(如 Linux 和 macOS)中,新的进程是通过 fork() 系统调用来创建的。这个系统调用会复制当前进程的所有状态(包括内存中的代码和数据),从而生成一个几乎完全相同的子进程,从父进程执行到的位置继续运行。因此,在这些系统上,你可以直接启动新进程而不需要特别处理模块导入逻辑。

然而,在 Windows 上,由于缺乏 fork() 系统调用,Python 使用一种叫做“spawn”的方法来启动新进程。具体来说,当一个新的进程被创建时,Python 需要重新执行你的脚本以初始化新的 Python 解释器实例。这意味着你的脚本会被再次从头开始执行,并且为了确定哪些代码应该在新的进程中运行,Python 需要依赖于 if __name__ == '__main__': 这样的结构来区分主程序入口点。

具体原因
如果没有 if __name__ == '__main__': 的保护,那么每次新进程启动时都会重新执行整个脚本文件,包括创建新的 Process 对象并调用 .start() 方法。这将导致每个新进程又尝试创建自己的子进程,进而触发新一轮的脚本重新执行,形成一个无限循环,造成无限递归创建子进程的现象。

关于进程 PID 的描述

当你运行上述示例时,你会注意到:

  • 两个子进程的 PID 和当前进程的 PID 是不同的。这是因为每个子进程都有自己独立的 PID。
  • 主进程的父进程 PID 通常是启动该脚本的程序的 PID,比如 PyCharm 或者命令行解释器(如 cmd 中运行 tasklist 命令可以看到相关进程信息)。

例如,在 Windows 中可以通过以下方法查看当前进程及其父进程的信息:

  • 打开任务管理器,查找对应的 Python 进程。
  • 或者在命令提示符窗口输入 tasklist 查看详细信息。
Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐