python 之 进程与线程区别、GIL锁产生背景及对Python性能的影响?python的多线程是假的,为啥还用多线程

本文围绕Python展开,介绍了进程与线程的区别,包括资源开销、通信方式等。阐述了GIL锁对Python性能的影响,解释了Python多线程在不同场景的适用性,说明了IO密集型用线程、计算密集型用多进程的原因,还介绍了进程间8种通信方式及并发、并行等概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、进程与线程区别
  • 根本区别
    进程是操作系统资源分配的基本单位。每个进程都有自己独立的地址空间、数据、堆栈和状态
    线程是处理器任务调度和执行的基本单位。一个进程可以有多个线程,这些线程共享进程的地址空间和资源
  • 资源开销(内存分配)
    进程创建新的进程需要分配独立的地址空间,有较大的字节。进程间切换和通信成本相对较高
    线程间共享地址空间和资源,因此线程的创建、切换和通信相对较接近,资源消耗较少
  • 通信方式
    进程间通信相对复杂,主要方式有管道、消息队列、信号量、共享内存等
    线程间通信更简单,可以直接访问其他线程的数据(因为它们共享地址空间),主要通过锁、信号量等同步原语来协调(也可以共享变量,共享内存,共享数据库,消息队列)
  • 隔离性与安全性:
    进程间有最大的隔离性,一个进程崩溃(例如访问了内存)一般不会影响其他进程。
    线程间共享进程资源,一个线程的错误可能影响同一进程的其他线程。
  • 使用场景
    IO密集型使用线程,计算密集型使用多进程
    进程适用于相互独立,需要较高安全性和隔离性的场景。
    线程适用于需要密集通信,或共享大量资源的场景,如服务器的请求处理
  • 补充
    协程:是一种用户态的轻量级线程,协程的调度完全由用户控制。
    协程拥有自己的寄存器上下文和栈,协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
二、什么是GIL锁?GIL对Python性能的影响
  • GIL全局解释锁
    全局解释锁: 每个线程在执行过程的过程都需要先获取GIL,确保在同一时刻只有一个线程可以执行字节码,目的是简化CPython的设计, 保证线程安全

  • 线程释放GIL锁的情況:

    1. 在I0操作等可能会引起阻塞的system call之前,可以暂时释放GIL,但在执行完毕后,必须重新获取GIL
    2. Python 3.x使用计时器(执行时间达到國值后,当前线程释放GIL)或Python 2.x, tickets计t数达到100

    python使用多进程是可以利用多核的CPU资源的。
    多线程爬取比单线程性能有提升,因为遇到I0阻塞会自动释放GIL锁
    多进程适合在 CPU 密集型操作(Cpu 操作指令比较多,如位数多的浮点运算)。
    多线程适合在 IO 密集型操作(读写数据操作较多的,比如爬虫)

  • GIL全局解释锁:目的是简化CPython的设计, 保证线程安全

    1. 保护内存管理:
      GIL保证了对Python对象的访问是线程安全的。Python使用引用计数来管理内存,而更新引用计数并不是一个原子操作。假如两个线程同时修改同一个对象的引用计数,可能会导致引用计数被错误地更新,进而引发内存泄漏或错误地回收了仍在使用的对象。GIL确保了每一时刻只有一个线程在执行Python字节码,从而避免了这种竞争条件
    2. 简化设计和实现:
      通过使用GIL,CPython解释器的实现大大简化。GIL消除了多线程环境中复杂的锁管理和同步问题。没有GIL,CPython的每个数据结构都需要单独管理锁,这将增加设计和实现的复杂性
三、为啥python的多线程是假的,还用多线程
  • 为什么说Python的多线程是“假”的:
    GIL是一个全局锁,它保证一次只有一个线程执行Python字节码。在多核CPU环境中,这意味着Python的多线程不能有效利用多个CPU核心。相反,尽管有多个线程,但在任何给定的时刻,只有一个线程在执行。这使得Python的多线程对于计算密集型任务效果不佳,因为它们无法真正地执行任务。
  1. 为什么仍然使用Python的多线程
    对于I/O密集型任务(如网络通信、文件I/O等),多线程仍然是非常有效的。
    在I/O密集型任务中,线程大部分时间都在等待外部资源
    比如等待文件读取写完成或网络响应。在此期间,CPU几乎处于空闲状态。
    通过使用多线程,当一个线程等待I/O操作完成时,GIL可以被释放,其他线程可以继续执行。
    这样,多线程可以在I /O密集型应用中提高程序的总体性能和响应能力
  2. 综上所述
    尽管Python的多线程因GIL的存在而受到了一定的限制(特别是对于计算密集型任务),它仍然在某些场景下是有用和有效的。
    选择是否使用多线程,以及如何使用,取决于具体的应用场景和性能需求。
四、为啥 IO密集型使用线程,计算密集型使用多进程
3.1 I/O密集型任务:

I/O密集型任务是指系统的性能瓶颈主要出现在I/O操作上,如文件操作、网络通信等

为什么使用线程??

  1. I/O 密集型任务中,CPU 的使用并不是瓶颈,主要时间消耗在等待 I/O 操作上。在此期间,CPU 大部分时间都是空闲的。
  2. 线程的创建和思考成本很小,且所有线程共享进程的内存空间,使得线程之间的通信和数据交换成本很低。
  3. 使用线程可以使一个进程内的多个线程在等待I/O操作时可以共享同一个资源,这样当一个线程在等待I/O时,其他线程可以继续执行
3.2 计算密集型任务:

计算密集型任务是指系统的性能瓶颈主要出现在CPU计算上,即任务需要大量的CPU计算时间

为什么使用多进程??

  1. 对于计算密集型任务,多进程可以有效地利用多核CPU的优势,因为每个进程可以在一个单独的CPU核上执行。
    进程间的独立性较强,一个进程的崩溃不会影响到其他进程。
    在解释全局器锁(GIL)的存在的环境下(例如CPython),多线程程序无法有效利用多核CPU。GIL是Python的一个互斥锁,它阻止多个线程同时执行Python字节码。
    密集型任务,这可能导致程序的效率大幅度降低。多进程则参与GIL的限制,每个进程都有自己的GIL和独立的内存空间,因此可以充分利用多核CPU。
五、进程间8种通信方式详解
  1. 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常
    是指父子进程关系
  2. 消息队列通信:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载
    无格式字节流以及缓冲区大小受限等缺点
  3. 信号量:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源
    时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
  4. 信号:信号是种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
  5. 共享内存通信:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内
    存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实来实现进程间的同步和通信。
  6. socket:套接口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信
为什么爬虫使用多线程?而不是多进程
  • 多进程占用资源多,进程间通讯不方便,爬虫很少用
  • 多线程/协程 在请求数据和解析数据的时候采用多线程,大部分时间都是单线程爬虫,大部分情况采用分布式
六、什么是并发和并行?同步和异步,阻塞和非阻塞的区别?

# 并发:同一时刻只能处理一个任务,但可以交替处理多个任务。(一个处理器同时处理多个任务〕
# 并行:同一时刻可以处理多个任务。(多个处理器或者是多核的处理器同时处理多个不同的任务)
# 类比:并发是一个人同时吃三个馒头,而并行是三个人同时吃三个馒头。

线程是并发,进程是并行;进程之间相互独立,是系统分配资源的最小单位,同一个线程中的所有线程共享资源。

阻塞:在执行一个操作时,不能做其他操作;
非阻塞:在执行一个操作时,能做其他操作。

阻塞和非阻塞关心的问题是:能不能做其他操作。

IO多路复用方式: select、poll、epoll
select : (socket最大连接数1024)不断轮询每个socket对象
epoll :(链接数量没有限制的) 给每个socket对象绑定一个回调函数

路由分发器: 匹配正则调用相应处理器类
indexhandler: 具体的页面

并发:同一个时间,单CPU只执行一个任务
并行:多CPU同时执行不同任务
同步和异步针对结果而言:
	同步: 对请求结果来说,上一步的操作必须执行完毕,下一步才能执行
	异步: 下一步的操作不需要等待上一步的完成
阻塞和非阻塞针对线程的状态而言:
	阻塞: 线程没有资源,因此挂起并不执行
	非阻塞: 线程有资源,一直运行
Tornado是异步非阻塞的框架

阻 塞 、 非 阻 塞
阻 塞 是 指 调 用 函 数 时 候 当 前 线 程 被 挂 起 。
非 阻 塞 是 指 调 用 函 数 时 候 当 前 线 程 不 会 被 挂 起 , 而 是 立 即 返 回 。

同 步 、 异 步
同 步 是 你 告 诉 老 婆 做 饭 , 然 后 老 婆 就 开 始 做 饭 , 你 就 一 直 等 待 饭 上 桌 。

异 步 是 , 你 告 诉 考 婆 让 她 去 做 饭 , 如 果 老 婆 同 意 会 立 即 返 回 1, 你 等 待 老 婆
做 好 , 你 可 以 干 其 他 事 情 , 做 好 之 后 老婆 总 有 通 知 你 的 方 式 〔( 比 如 你 在 玩 电
脑 你 老 婆 揪 着 你 的 耳 朵 去 吃 饭 ) 。 如 果 老 婆 不 做 , 她 也 会 立 刻 通 知 你 , 返 回
-1, 这 时 候 也 不 会 阻 塞 。 当 然 还 有 一 种 是 你 老 婆 答 应 你 了 , 结 果 过 了 一 会 她
大 喊 一 声 , 劳 资 不 做 了 ! 但 也 会 通 知 你 。 但 是 最 后 吃 饭 到 你 需 要 自 己 去 先 获
取 到 老 婆 的 通 知 然 后 再 去 端 饭 。

同 步 和 异 步 关 注 的 是 获 取 结 果 的 方 式 。
同 步 是 获 取 到 结 果 之 后 才 进 行 下 一 步操 作 ,
阻塞 非 阻塞 关 注 的 是 接 口 时 当 前 线 程 的 状 态 ,
同 步 可 以 调 用 际 塞 也可 以 非 阻 塞 。 异 步 是 调 用 非 阻 塞 接 口

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风华浪浪

讨个老婆本呗

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值