线程与进程

qing meng 发布于 2025-03-03 57 次阅读


一、进程(Process)和线程(Thread)的基本概念

1. 进程(Process)

  • 定义:进程是操作系统分配资源的最小单位,每个进程有独立的内存空间、文件句柄和系统资源。
  • 特点
    • 隔离性:进程之间相互隔离,一个进程崩溃不会影响其他进程。
    • 资源开销大:创建和切换进程需要较高的系统资源。
    • 适用于 CPU 密集型任务:多进程可充分利用多核 CPU 的并行计算能力。

类比:一个工厂,一个车间,一名工人。 == 一个cpu,一个进程,一个线程

2. 线程(Thread)

  • 定义:线程是操作系统调度的最小单位,属于同一进程的多个线程共享进程的内存和资源。
  • 特点
    • 轻量级:创建和切换线程的开销远小于进程。
    • 共享内存:线程之间可以直接读写同一进程的数据,但需要同步机制(如锁)避免冲突。
    • 适用于 IO 密集型任务:例如网络请求、文件读写等需要等待外部操作的任务。

类比:一个工厂,一个车间,一名工人。 == 一个cpu,一个进程,一个线程

二、全局解释器锁(GIL)

1. GIL 是什么?

  • 定义:GIL(Global Interpreter Lock)是 Python 解释器(CPython)中的一个全局互斥锁,用于保护 Python 对象的内存安全。
  • 作用:同一时刻只能有一个线程执行 Python 字节码,避免多线程并发操作导致的数据不一致问题。

三,线程

1.start()当前线程准备就绪(等待cpu调度,具体时间由cpu决定)

2.join()等待子线程结束,主线程才会结束

setDaemon守护线程

3.线程安全

Lock同步锁:Lock不支持嵌套锁
RLock递归锁:Rlock支持嵌套锁

4.线程池

四、进程

1.概念

multiprocessiong模块操纵进程
三种模式:
fork(mac):用来拷贝
spawn(windows):如果想创建一个子进程,想让这个子进程去干某个事,它会在内部创建一个python解释器,让python解释器去运行想要实现某个功能
forkserver(unix):父进程启动一个“服务器进程”,所有子进程都由该服务器通过 fork() 创建,避免了直接复制多线程父进程的风险。
.start()
.join()与线程功能相同

2.daemon守护线程

daemon=Ture 设置为守护线程,主进程执行完毕后,子进程也自动关闭

daemon=False 设置为非守护进程,主进程等待子进程

3.进程锁

4.进程池

import time
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
import multiprocessing

def task(num):
    print('开始',num)
    time.sleep(2)

"""
if __name__ =="__main__":
    pool = ProcessPoolExecutor(4)
    for i in range(10):
        pool.submit(task,i)
        # #等待进程池中的任务完成后,再继续往下进行
        # pool.shutdown(True)
        # print(1)
"""

def done(ree):
    print(multiprocessing.current_process())
    time.sleep(1)
    print(ree.result)
    time.sleep(1)

if __name__ =="__main__":
    pool = ProcessPoolExecutor(4)
    for i in range(10):
        f = pool.submit(task,i)
        f.add_done_callback(done) #done的调用由主进程处理(与线程池不同)

    print(multiprocessing.current_process())
    pool.shutdown(True)
"""
如果在进程池中的进程锁,则要基于Manger的Lock和Rlock

"""

为什么要使用Manger的Lock和Rlock,而使用multiprocessing和threading的就会报错

1. 进程间共享内存的隔离性
  • 每个进程有独立的内存空间,普通锁(如multiprocessing.Lock)仅在创建它的进程内有效。
  • 当进程池的子进程尝试使用主进程创建的锁时,由于内存隔离,子进程无法直接访问主进程的锁对象,导致同步失败或报错。
2. 序列化与传递问题
  • 进程池中的任务参数需要能被Pickle序列化。普通的multiprocessing.Lock对象不可被序列化,传递时会引发PicklingError
  • Manager创建的锁实际是一个代理对象(Proxy),它通过IPC(进程间通信)连接到Manager服务器进程中的真实锁,代理对象可以被序列化,从而安全传递到子进程。
3. Manager的中间层作用
  • Manager启动了一个独立的服务器进程,用于管理共享对象(如锁)。
  • 所有进程通过代理访问同一把锁,确保锁的状态在所有进程间同步,无论子进程以fork还是spawn方式启动。
4. threading.Lock的线程局限性
  • threading.Lock仅用于同一进程内的线程同步,无法跨进程工作。在进程池中使用它会导致未定义行为或错误。

不同的人,不同的道,入目不同的景
最后更新于 2025-03-07