python基础之进程学习

一、 并发与并行

**并发:**单个 CPU 处理多个任务。各个任务交替执行一段时间。

**并行 :**多个 CPU 同时执行多个任务

二、 什么是进程

进程是计算机中正在运行的程序的实例。每个进程都有自己独立的内存空间、系统资源和执行环境。简单来说:

  • 独立性:进程之间相互隔离,一个进程崩溃不会影响其他进程

  • 资源分配:操作系统为每个进程分配独立的内存、文件句柄等资源

  • 并发执行:多个进程可以同时运行,实现真正的并行(在多核CPU上)

Python 创建进程的方法

1. 使用 multiprocessing 模块创建进程(推荐)

这是 Python 中最常用的创建进程方式。

基本用法:
python 复制代码
# 1. 使用 multiprocessing 模块(推荐)

import multiprocessing
import os

def worker(*args, **kwargs):
    print(f"进程{args[0]}(PID={os.getpid()})正在执行")
    print(f"参数:{args}, {kwargs}")
    return f"进程{args[0]}执行完毕"

if __name__ == '__main__':
    p = multiprocessing.Process(target=worker, args=("worker1",), kwargs={"name": "Alice"})
    p.start()
    p.join()
    print("主进程结束")

multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)

  • group :应当始终为 None ,它的存在仅是为了与 threading.Thread 兼容。
  • target :由 run() 方法来发起调用的可调用对象,默认为 None
  • name: 进程名称,默认为 None 则自动分配。
  • **args:**针对目标调用的参数元组。
  • **kwargs:**针对目标调用的关键字参数字典。
  • daemon: 是否为守护进程**,True** 或 False 。默认为None则继承父进程。

Process 的属性和方法与其他常用方法

  • n am **e:**获取进程名称。
  • p **id:**获取进程号。
  • daemon **:**判断或设置进程是否为守护进程。
  • e **xitcode:**获取子进程的退出状态码。
  • s tar t(): 启动进程,调用传入 target 的对象。start() 只能被调用一次。
  • run(): 默认调用传入 target 的对象,如果子类化了 Process,可以重写此方法来自定义行为。
  • join([timeout]): 阻塞主进程,直到子进程结束或超时。timeout参数可选,意为阻塞多少秒。
  • **terminate():**强制终止子进程。
  • kill(): 杀死进程,与 terminate() 类似,但更彻底。
  • **is_alive():**检查进程是否仍在运行。
  • **os.getpid():**获取当前进程编号。
  • **os.getppid():**获取当前进程的父进程编号。

案例:同时读写文件

注意:在Windows上执行要加上if name == "main"

python 复制代码
# 1. 使用 multiprocessing 模块(推荐)

import multiprocessing
import time
from datetime import datetime


def get_time():
    current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
    return current_time


def write_file():
    with open('lgo.txt', 'at', encoding='utf-8') as f:
        while True:
            f.write(f'{get_time()}执行写入操作\n')
            f.flush()  # 刷新缓冲区
            time.sleep(1)


def read_file():
    time.sleep(2)  # 等待写入进程先开始
    try:
        with open('lgo.txt', 'r', encoding='utf-8') as f:
            while True:
                line = f.readline()
                if not line:
                    time.sleep(1)
                    continue
                print(f"{get_time()}:执行读取操作,读取到文件内容:{line.strip()}")
    except FileNotFoundError:
        print(f"{get_time()}:文件不存在,等待写入进程写入文件")
        time.sleep(2)


if __name__ == '__main__':
    p1 = multiprocessing.Process(name='write_file', target=write_file)
    p1.start()
    p = multiprocessing.Process(name='read_file', target=read_file)
    p.start()

2、自定义Process子类创建进程

python 复制代码
import multiprocessing
import os
import time


class worker(multiprocessing.Process):
    def run(self):
        current_process = multiprocessing.current_process()
        print(f'进程ID: {os.getpid()}')
        print(f'父进程ID: {os.getppid()}')
        print(f'当前进程名: {current_process.name}')
        print(f'self.name: {self.name}')
        print(f'启动时间: {time.ctime()}')
        print('-' * 50)


if __name__ == '__main__':
    print(f'主进程ID: {os.getpid()}')
    print(f'主进程名: {multiprocessing.current_process().name}')
    print('=' * 50)

    for i in range(3):  # 改为3个进程便于观察
        p = worker(name=f'worker{i}')
        p.start()
        p.join()  # 等待子进程结束

3、进程池 :当需要启动大量子进程时,可以使用进程池。

进程池的创建

multiprocessing.Pool([processes[,initializer[,initargs[,maxtasksperchild[,context]]]]])

  • processes :要使用的工作进程数量。如果 processesNone 则使用 os.cpu_count() 所返回的数值。
  • initializer 如果不为 None ,则每个工作进程将会在启动时调用 initializer(*initargs)
  • maxtasksperchild: 一个工作进程在它退出或被一个新的工作进程代替之前能完成的任务数量,为了释放未使用的资源。默认的 maxtasksperchildNone,意味着工作进程寿与池齐。
  • context: 可被用于指定启动的工作进程的上下文。通常一个进程池是使用函数 multiprocessing.Pool() 或者一个上下文对象的 Pool() 方法创建的。

注意:进程池对象的方法只有创建它的进程能够调用。

使用时一般只指定 processes 参数。

进程池的常用方法

  • apply(func[, args[, kwds]]) 使用 args 参数以及 kwds 命名参数同步调用 func , 在返回结果前阻塞。另外 func 只会在一个进程池中的一个工作进程中执行。
  • apply_async(func[, args[, kwds[, callback[, error_callback]]]]) 使用 args 参数以及 kwds 命名参数异步调用 func ,并立即返回一个 AsyncResult 对象,不会阻塞。可以通过 callback 获取结果和通过 error_callback 处理异常。
  • close() **:**阻止后续任务提交到进程池,当所有任务执行完成后,工作进程会退出。
  • terminate() 不必等待未完成的任务,立即停止工作进程。当进程池对象被垃圾回收时,会立即调用 terminate()
  • join() 阻塞主进程,等待工作进程结束。调用 join() 前必须先调用 close() 或者 terminate()

案列 :

同步(阻塞式)

python 复制代码
import multiprocessing
import os
import time


def func():
    for i in range(10):
        print(f"<Process {os.getpid()}>: {i}")
        time.sleep(1)

if __name__ == "__main__":
    process_count = 3
    pool = multiprocessing.Pool(process_count)
    for i in range(process_count):
        pool.apply(func)  #同步   阻塞式
        # pool.apply_async(func) #异步  非阻塞式
    # pool.close()
    # pool.join()
    print(f"主进程名称:{multiprocessing.current_process().name} 执行结束")
python 复制代码
<Process 26572>: 0
<Process 26572>: 1
<Process 26572>: 2
<Process 26572>: 3
<Process 26572>: 4
<Process 26572>: 5
<Process 26572>: 6
<Process 26572>: 7
<Process 26572>: 8
<Process 26572>: 9
<Process 13160>: 0
<Process 13160>: 1
<Process 13160>: 2
<Process 13160>: 3
<Process 13160>: 4
<Process 13160>: 5
<Process 13160>: 6
<Process 13160>: 7
<Process 13160>: 8
<Process 13160>: 9
<Process 6844>: 0
<Process 6844>: 1
<Process 6844>: 2
<Process 6844>: 3
<Process 6844>: 4
<Process 6844>: 5
<Process 6844>: 6
<Process 6844>: 7
<Process 6844>: 8
<Process 6844>: 9
主进程名称:MainProcess 执行结束

Process finished with exit code 0

异步(非阻塞式)

python 复制代码
import multiprocessing
import os
import time


def func():
    for i in range(10):
        print(f"<Process {os.getpid()}>: {i}")
        time.sleep(1)

if __name__ == "__main__":
    process_count = 3
    pool = multiprocessing.Pool(process_count)
    for i in range(process_count):
        #pool.apply(func)  #同步   阻塞式
        pool.apply_async(func) #异步  非阻塞式
    pool.close()
    pool.join()
    print(f"主进程名称:{multiprocessing.current_process().name} 执行结束")
python 复制代码
<Process 20416>: 0
<Process 26128>: 0
<Process 28980>: 0
<Process 20416>: 1
<Process 26128>: 1
<Process 28980>: 1
<Process 20416>: 2
<Process 26128>: 2
<Process 28980>: 2
<Process 20416>: 3
<Process 26128>: 3
<Process 28980>: 3
<Process 20416>: 4
<Process 26128>: 4
<Process 28980>: 4
<Process 20416>: 5
<Process 26128>: 5
<Process 28980>: 5
<Process 20416>: 6
<Process 26128>: 6
<Process 28980>: 6
<Process 20416>: 7
<Process 26128>: 7
<Process 28980>: 7
<Process 20416>: 8
<Process 26128>: 8
<Process 28980>: 8
<Process 20416>: 9
<Process 26128>: 9
<Process 28980>: 9
主进程名称:MainProcess 执行结束

三、进程间的通信

1、 ​​​​​​​****进程间不共享全局变量 :****子进程向传入的列表中添加元素,最终发现主进程与子进程之间的列表结果不同:

python 复制代码
import multiprocessing
import os


def func(list):
    for i in range(5):
        list.append(i)
        print(f'process id{os.getpid()},list: {list}')

if __name__ == '__main__':
    list = []
    p1= multiprocessing.Process(target=func, args=(list,))
    p2= multiprocessing.Process(target=func, args=(list,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print(f'process name: {multiprocessing.current_process().name}, Final list: {list}')
python 复制代码
process id13412,list: [0]
process id13412,list: [0, 1]
process id13412,list: [0, 1, 2]
process id13412,list: [0, 1, 2, 3]
process id13412,list: [0, 1, 2, 3, 4]
process id7152,list: [0]
process id7152,list: [0, 1]
process id7152,list: [0, 1, 2]
process id7152,list: [0, 1, 2, 3]
process id7152,list: [0, 1, 2, 3, 4]
process name: MainProcess, Final list: []
复制代码
使用 multiprocessing.Manager 来共享列表
python 复制代码
import multiprocessing
import os


def func(list):
    for i in range(5):
        list.append(i)
        print(f'process id{os.getpid()},list: {list}')

if __name__ == '__main__':
    # list = []
    # 重要:使用 multiprocessing.Manager 来共享列表
    list = multiprocessing.Manager().list() # 创建可在进程间共享的列表
    p1= multiprocessing.Process(target=func, args=(list,))
    p2= multiprocessing.Process(target=func, args=(list,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print(f'process name: {multiprocessing.current_process().name}, Final list: {list}')
python 复制代码
process id17400,list: [0]
process id17400,list: [0, 1]
process id17400,list: [0, 1, 2]
process id17400,list: [0, 1, 2, 3]
process id17400,list: [0, 1, 2, 3, 4]
process id28128,list: [0, 1, 2, 3, 4, 0]
process id28128,list: [0, 1, 2, 3, 4, 0, 1]
process id28128,list: [0, 1, 2, 3, 4, 0, 1, 2]
process id28128,list: [0, 1, 2, 3, 4, 0, 1, 2, 3]
process id28128,list: [0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
process name: MainProcess, Final list: [0, 1, 2, 3, 4, 0, 1, 2, 3, 4]

2、进程池之间使用 Manager().Queue 通信

python 复制代码
import multiprocessing
import os
import random
import time

# generate random numbers and put them into a queue
def func1(queue):
    while True:
        random_num = random.randint(1, 50)
        queue.put(random_num)
        print(f"进程ID:{os.getpid()}放入{random_num}到队列中")
        time.sleep(1)

def func2(queue):
    while True:
        if not queue.empty():
            num = queue.get()
            print(f"进程ID:{os.getpid()}从队列中取出{num}")
            time.sleep(1)

if __name__ == '__main__':
    # 通过Manager().Queue()创建一个进程
    queue = multiprocessing.Manager().Queue()
    #通过Process()创建两个进程
    # p1 = multiprocessing.Process(target=func1, args=(queue,))
    # p2 = multiprocessing.Process(target=func2, args=(queue,))
    # p1.start()
    # p2.start()
    # p1.join()
    # p2.join()
    # 或者使用Pool()创建进程池
    pool=multiprocessing.Pool(2)
    pool.apply_async(func1,args=(queue,))
    pool.apply_async(func2,args=(queue,))
    pool.close()
    pool.join()
相关推荐
程序员大雄学编程2 小时前
定积分的几何应用(一):平面图形面积计算详解
开发语言·python·数学·平面·微积分
小兵张健2 小时前
Java + Spring 到 Python + FastAPI (一)
java·python·spring
2401_841495643 小时前
【自然语言处理】基于统计基的句子边界检测算法
人工智能·python·算法·机器学习·自然语言处理·统计学习·句子边界检测算法
程序员爱钓鱼3 小时前
Python编程实战 - Python实用工具与库 - 操作Word:python-docx
后端·python
程序员爱钓鱼3 小时前
Python编程实战 - Python实用工具与库 - 操作PDF:pdfplumber、PyPDF2
后端·python
啾啾啾6663 小时前
连接一个新的服务器时,打开PyCharm时报错:报错内容是服务器磁盘或配额满了
python·pycharm
长不大的蜡笔小新3 小时前
掌握NumPy:ndarray核心特性与创建
开发语言·python·numpy
luoganttcc3 小时前
已知 空间 三个 A,B C 点 ,求 顺序 经过 A B C 三点 圆弧 轨迹 ,给出 python 代码 并且 画出图像
c语言·开发语言·python
Q_Q5110082854 小时前
python+django/flask的图书馆管理系统vue
spring boot·python·django·flask·node.js·php