0
点赞
收藏
分享

微信扫一扫

多线程和多进程

alonwang 08-25 21:00 阅读 7

一、概念

多线程(threading):同一进程里开多条“轻量级执行路线”,大家共享同一块内存。

多进程(multiprocessing):启动多个独立 Python 解释器,彼此内存隔离,靠队列/管道通信。

二、多线程和多进程的区别

对比点

多线程

多进程

内存

共享

独立

开销

启动快、占内存小

启动慢、占内存大

CPU并行

受GIL限制

不受GIL限制,能多核并行

适用任务

I/O密集型(如网络、文件)

CPU密集型(如计算)

通信

直接读写变量(需加锁)

Queue / Pipe / Manager

调试难度

死锁、竞态难抓

进程崩溃互不影响,但调试也麻烦

三、多线程基本语法

3.1 导包

import threading

3.2 Thread类

threading 模块里提供了一个类叫 Thread(注意大写 T)

这个类就是“线程模具”——你拿它就能“印”出一条新线程

3.3 Thread类中的关键参数

参数名

类型/示例

作用

target

可调用对象,如 say

线程启动后要执行的函数/方法

args

元组 ,如(3,)

传给 target 的位置参数

kwargs

字典 ,如{'x': 1}

传给 target 的关键字参数

name

字符串 ,如'Worker-1'

给线程起名字,方便调试

daemon

True / False

设为 True 时,主线程退出后子线程立即被强制结束;否则主线程会等待子线程


四、多进程基本语法

4.1 导包

import multiprocessing

4.2 Process和Pool 类

Process 类 :一条一条手动开进程

Pool 类:进程池,一行代码并行 map

4.3 Process和Pool的关键参数

4.3.1 Process的关键参数

from multiprocessing import Process, Lock, Value

p = Process(
        group=None,          # 预留,不用管
        target=None,         # 【必填】子进程要执行的函数
        args=(),             # 给 target 的位置参数(元组)
        kwargs={},           # 给 target 的关键字参数(字典)
        name=None,           # 子进程名字,方便调试
        daemon=False         # True 时主进程结束立刻杀子进程
)

90 % 场景只需写:

p = Process(target=func, args=(x, y))

4.3.2 Pool的关键参数

from multiprocessing import Pool

with Pool(
        processes=None,      # 工作进程数,None = 用 CPU 核数
        initializer=None,    # 每个进程启动时先跑一次的初始化函数
        initargs=(),         # initializer 的位置参数
        maxtasksperchild=None # 每个子进程做完多少任务后重启,防内存泄漏
) as pool:
    pool.map(func, iterable)

常用 1 行写法:Pool()Pool(4) 直接开 4 个进程

五、多线性和多进程代码举例(Python3)

5.1 多线程最小例子

import threading      # 导入线程模块,提供 Thread、Lock 等工具

# 线程要执行的函数:打印 n 次问候,并标明当前线程名字
def say(n):
    """
    n: int
       需要打印的次数
    """
    for i in range(n):
        # threading.current_thread().name 获取当前线程的名字
        print('hi from', threading.current_thread().name)

# 创建两条线程对象:
#   target=say   告诉线程启动后去执行 say 函数
#   args=(3,)    把元组 (3,) 作为位置参数传给 say,因此 say 将收到 n=3
t1 = threading.Thread(target=say, args=(3,))
t2 = threading.Thread(target=say, args=(2,))

# 启动线程:真正开始并发执行
t1.start()   # 子线程 1 开始跑 say(3)
t2.start()   # 子线程 2 开始跑 say(2)

# 等待线程结束:阻塞主线程,直到 t1、t2 都跑完
t1.join()
t2.join()

# 所有子线程结束后主线程继续
print('main done')

5.2 多进程最小例子

# -*- coding: utf-8 -*-
"""
多进程最小示例:创建两条子进程并发打印问候语
运行环境:Windows / Linux / macOS
注意:Windows 下必须放在 if __name__ == '__main__': 中
"""

import multiprocessing as mp      # 导入 multiprocessing 模块,简写为 mp

# 子进程要执行的函数
# 这个函数必须“顶层可导入”,否则子进程会找不到
def say(n: int):
    """
    n : int
        当前进程需要打印的次数
    """
    # mp.current_process().name 获取当前子进程的名字(默认形如 "Process-1")
    for i in range(n):
        print('hi from', mp.current_process().name)


# ---------- 主进程入口 ----------
if __name__ == '__main__':
    # 创建两条进程对象:
    #   target=say   子进程启动后去执行 say 函数
    #   args=(3,)    把位置参数 3 传给 say,因此子进程 1 会打印 3 次
    p1 = mp.Process(target=say, args=(3,), name='Worker-1')  # 可自定义名字
    p2 = mp.Process(target=say, args=(2,), name='Worker-2')

    # 启动子进程:真正开始并行(操作系统层面)
    p1.start()  # 子进程 1 开始跑 say(3)
    p2.start()  # 子进程 2 开始跑 say(2)

    # 阻塞主进程,等待两条子进程都结束
    p1.join()
    p2.join()

    # 所有子进程退出后,主进程才继续
    print('main done')

六、线程锁和进程锁

# -*- coding: utf-8 -*-
"""
演示「线程锁」和「进程锁」的共享计数示例。
主程序未写出,这里只给出函数与锁定义,方便后续调用。
"""

import threading          # 线程相关
import multiprocessing    # 进程相关
import time               # 仅作演示延时(可删)

# ----------------- 锁对象 -----------------
# 1. 线程锁:保护多线程共享变量
lock1 = threading.Lock()

# 2. 进程锁:保护多进程共享变量(Value / Array / Manager)
lock2 = multiprocessing.Lock()

# ----------------- 计数函数 -----------------
def counter(lock, num, cnt):
    """
    在并发环境下安全地把计数器 cnt 累加 num 次。

    参数
    ----
    lock : threading.Lock 或 multiprocessing.Lock
        用于互斥,防止同时修改计数器。
    num : int
        累加次数。
    cnt : int(线程) 或 multiprocessing.Value('i', 0)(进程)
        共享计数器。线程可直接用全局 int;进程必须借助 Value/Array/Manager。
    """
    for _ in range(num):
        with lock:              # 自动 acquire / release
            cnt.value += 1      # 对 Value 对象取值/赋值用 .value
            # 如果是线程共享的普通 int,直接 cnt += 1 即可
            # time.sleep(0)     # 可加极短 sleep 放大并发冲突,方便实验

举报

相关推荐

0 条评论