AI项目博客
记录 AI 工程实践与大模型应用

Python 学习笔记 30|使用 Process 创建进程

3. 使用 Process 创建进程

使用multiprocessing.Process类创建进程对象。

import os
import time
from multiprocessing import Process
print(100, __name__, os.getpid())

# 定义一个 speak 函数,功能是:每隔一秒说话一次(一共说话10次)
def speak():
    for index in range(10):
        print(f'我在说话{index}, 进程pid是:{os.getpid()}, 我的父进程是:{os.getppid()}')
        time.sleep(1)

# 定义一个 study 函数,功能是:每隔一秒学习一次(一共学习15次)
def study():
    for index in range(15):
        print(f'我在学习{index}, 进程pid是:{os.getpid()}, 我的父进程是:{os.getppid()}')
        time.sleep(1)

# 注意:一定要写 if __name__ == '__main__' 这个判断,原因如下:
#   1.当创建子进程时,Python 并不会把父进程内存里的 speak 函数直接交给子进程。
#   2.Python会启动一个全新的 Python 解释器进程,重新执行当前的 .py 文件(作为模块)。
#   3.在执行过程中,重新定义出一个 speak 函数,交给子进程。
if __name__ == '__main__':
    print('我是主进程中的【第一行】打印')
    # 创建两个 Process 类的实例对象(进程对象),分别是 p1 和 p2。
    # 注意点1:p1 和 p2 就对应着以后的两个子进程,在创建它们的时候,就要指定好他们要执行的任务。
    # 注意点2:此时的 p1 和 p2 只是代码层面的两个进程对象,操作系统还没有创建 p1 和 p2 两个进程。
    p1 = Process(target=speak)
    p2 = Process(target=study)

    # 调用进程对象的 start 方法,会立刻向操作系统申请一个进程,并且会将该进程交由操作系统进行调度。
    p1.start()
    p2.start()

    print('我是主进程中的【最后一行】打印')

⚠️注意点:

在 Windows 中使用 multiprocessing 必须加上 if __name__ == ‘__main__’:,原因是:创建子进程时,Python 不会直接拷贝内存里的函数给子进程。Python 会启动一个全新的解释器,重新执行当前的.py文件作为模块。如果不加判断,会无限递归创建子进程。

4. 关于 Process 的参数

在实例化 Process 时,可以传递以下参数:

🔸group: 默认值为None(应当始终为None)。

🔸target:子进程要执行的可调用对象,默认值为 None。

🔸name: 进程名称,默认为 None ,如果设置为 None,Python 会自动分配名字。

🔸args: 给 target 传的位置参数(元组)

🔸kwargs:给 target 传的关键字参数(字典)。

🔸daemon:标记进程是否为守护进程,取值为布尔值(默认为 None,表示从创建方继承)。

备注:可以使用 current_process().name 获取当前进程的名字。

import os
import time
from multiprocessing import Process, current_process

def speak(a, b, msg):
    for index in range(10):
        print(f'{msg}--{a}--{b}--{current_process().name}--我在说话{index}, 进程pid是:{os.getpid()}, 我的父进程是:{os.getppid()}')
        time.sleep(1)

def study():
    for index in range(15):
        print(f'我在学习{index}, 进程pid是:{os.getpid()}, 我的父进程是:{os.getppid()}')
        time.sleep(1)

if __name__ == '__main__':
    print('我是主进程中的【第一行】打印')
    p1 = Process(target=speak, name='说话进程', args=(666, 888), kwargs={'msg':''})
    p2 = Process(target=study)
    # print(p1.name)
    # print(p2.name)
    p1.start()
    p2.start()
    print('我是主进程中的【最后一行】打印')

5. 进程控制

1️⃣Lock(进程锁)

为了防止多个进程同时打印或操作同一资源导致数据错乱,可以使用 Lock。

传统用法:

lock.acquire() # 上锁
# ... 临界区代码 ...
lock.release() # 释放锁

上下文管理器用法 (推荐):

with lock:
    # 自动上锁,退出块时自动释放
    # 好处:即便发生异常也能保证释放锁,避免死锁

示例代码:

import os
import time
from multiprocessing import Process, Lock

def speak(lock):
    for index in range(10):
        # 上锁:如果锁是空闲的,立刻上锁,继续往下执行;如果锁被别人拿着:当前进程会阻塞等待
        lock.acquire()
        print('好好', end='')
        print('学习', end='')
        print('天天', end='')
        print('向上')
        # 释放锁:acquire 和 release 必须成对出现,否则会永远卡住(死等)
        lock.release()
        time.sleep(1)

def study(lock):
    for index in range(15):
        # with lock: 会自动完成两件事:
        #   (1).进入前:自动执行 lock.acquire() 上锁
        #   (2).离开后:自动执行 lock.release() 释放锁
        # 好处:即便代码块里发生异常,也能保证释放锁,避免“卡死”
        with lock:
            print('A', end='')
            print('B', end='')
            print('C', end='')
            print('D')
        time.sleep(1)

if __name__ == '__main__':
    print('我是主进程中的【第一行】打印')
    lock = Lock()
    p1 = Process(target=speak, args=(lock,))
    p2 = Process(target=study, args=(lock,))
    p1.start()
    p2.start()
    print('我是主进程中的【最后一行】打印')

传统 Lock 在面对多次上锁时,会产生死锁状态,解决办法是使用 RLock,示例代码:

import os
import time
from multiprocessing import Process, Lock, RLock

def speak(lock):
    for index in range(10):
        lock.acquire()
        lock.acquire()
        print('好好', end='')
        print('学习', end='')
        print('天天', end='')
        print('向上')
        lock.release()
        lock.release()
        time.sleep(1)

def study(lock):
    for index in range(15):
        with lock:
            print('A', end='')
            print('B', end='')
            print('C', end='')
            print('D')
        time.sleep(1)

if __name__ == '__main__':
    print('我是主进程中的【第一行】打印')
    lock = RLock()
    p1 = Process(target=speak, args=(lock,))
    p2 = Process(target=study, args=(lock,))
    p1.start()
    p2.start()
    print('我是主进程中的【最后一行】打印')

2️⃣ join 方法

  • 作用:阻塞当前进程(通常是主进程),直到调用 join 的那个进程执行完毕。
  • 参数:join(timeout),timeout 为超时时间(秒)。如果时间到了进程还没结束,主进程就不等了,会继续执行。
  • 注意:join 必须在 start() 之后调用。
# join 方法的作用:阻塞当前进程,等 join 前面的进程执行完,再继续往下执行。
# join(timeout),其中 timeout 是可选参数,表示等多久,单位是秒。

# 注意点:
#   1.p.join() 不是让进程 p 等,而是让“执行这行 join 代码的那个进程”去等,等的是 p 这个进程。
#   2.当达到了 timeout 所指定的时间后,进程并不会终止,只是“不再等”了。
#   3.join 必须在 start 之后
import os
import time
from multiprocessing import Process

def speak():
    for index in range(10):
        print(f'我在说话{index}, 进程pid是:{os.getpid()}, 我的父进程是:{os.getppid()}')
        time.sleep(1)

def study():
    for index in range(15):
        print(f'我在学习{index}, 进程pid是:{os.getpid()}, 我的父进程是:{os.getppid()}')
        time.sleep(1)

if __name__ == '__main__':
    print('我是主进程中的【第一行】打印')
    p1 = Process(target=speak)
    p2 = Process(target=study)
    p1.start()
    # 让主进程等 p1 5秒钟
    p1.join(5)

    p2.start()
    # p1.join() # 让主进程等 p1 进程执行完毕后,主进程再继续执行。
    # p2.join() # 让主进程等 p2 进程执行完毕后,主进程再继续执行。
    print('我是主进程中的【最后一行】打印')

3️⃣terminate 方法

  • 作用:向操作系统申请强制终止进程。
  • 注意:使用 terminate 终止进程,不会执行 finally 代码块。
  • 辅助方法:is_alive() 可用于查看进程是否还“活着”。
import os
import time
from multiprocessing import Process

def speak():
    try:
        for index in range(10):
            print(f'我在说话{index}, 进程pid是:{os.getpid()}, 我的父进程是:{os.getppid()}')
            time.sleep(1)
    # 注意:使用 terminate 终止进程,不会引起 finally 执行!
    finally:
        print('我是finally里的逻辑')

def study():
    for index in range(15):
        print(f'我在学习{index}, 进程pid是:{os.getpid()}, 我的父进程是:{os.getppid()}')
        time.sleep(1)

if __name__ == '__main__':
    print('我是主进程中的【第一行】打印')
    p1 = Process(target=speak)
    p2 = Process(target=study)
    p1.start()
    p2.start()

    time.sleep(3)
    print('我是主进程,我准备强制终止p1进程........')
    # 向操作系统申请强制终止p1进程
    p1.terminate()
    # 等操作系统彻底终止掉了p1进程
    p1.join()
    # 看一下p1进程是否“活着”
    print(p1.is_alive())

    print('我是主进程中的【最后一行】打印')

4️⃣ 守护进程 (Daemon)

什么是守护进程?

一种 “依附于主进程存在的子进程”,一旦主进程结束,它就会被自动终止。简言之:主进程一死,守护进程必跟着死。

守护进程的使用场景:

  • 后台监控类任务
  • 日志 / 统计 / 采样 类任务
  • 辅助型“陪跑任务”

注意点:

  • 守护进程必须是 子进程。
  • 主进程结束,守护进程也会随之结束。
  • 守护进程中,不允许再创建新的子进程。
  • 必须在 start 之前,start()之后,不能再设置daemon。
import os
import time
from multiprocessing import Process

def monitor():
    while True:
        try:
            with open('log.txt', 'r', encoding='utf-8') as file:
                lines = sum(1 for _ in file)
        except FileNotFoundError:
            lines = 0
        print(f'我是【守护进程({os.getpid()})】,log.txt 共有{lines}行')
        time.sleep(1)

def test():
    for index in range(30):
        print(f'我是test({os.getpid()})')
        time.sleep(1)

if __name__ == '__main__':
    print(f'我是主进程({os.getpid()})中的【第一行】代码')

    p1 = Process(target=monitor, daemon=True)
    p2 = Process(target=test)

    p1.start()
    p2.start()

    # 向文件中写入数据
    with open('log.txt', 'a', encoding='utf-8') as file:
        for index in range(10):
            file.write(f'{index}n')
            file.flush()
            time.sleep(1)

    print(f'我是主进程({os.getpid()})中的【最后一行】代码')
赞(0)
未经允许不得转载:AI项目博客 » Python 学习笔记 30|使用 Process 创建进程

评论 抢沙发

AI项目博客

专注 AI 工程化落地 —— 覆盖智能体、大模型、Java AI 与 Python 的学习笔记,以及 AI 平台的项目实战记录。

关于本站文章归档

登录

找回密码

注册