重新看看 GIL的问题
list 、 dict 等内建对象是 线程安全 的吗?
在 Python 层面,list 、dict 等内建对象是线程安全的,这是最基本的常识。研究 list、dict 等内建对象源码时,我们并没有看到任何 互斥锁 的痕迹,这多少有点令人意外。
Python 虚拟机维护了一个 全局锁 ,这就是众所周知的 GIL。Python 线程想在虚拟机中执行字节码,必须取得全局锁。这样一来,不管任何时刻,只有一个线程在虚拟机中运行。那么,虚拟机如何交替执行不同线程呢?
Python 线程调度实现方式参考了操作系统进程调度中 时间片 的思路,只不过将时间片换成 字节码 。当一个线程取得 GIL 全局锁并开始执行字节码时,对已执行字节码进行计数。当执行字节码达到一定数量 (比如 100 条) 时,线程主动释放 GIL 全局锁并唤醒其他线程。其他等待 GIL 全局锁的线程取得锁后,将得到虚拟机控制权并开始执行。因此,虚拟机就像一颗软件 CPU ,Python 线程交替在虚拟机上执行
在 GIL 的束缚下,Python 虚拟机同一时刻只能执行一个线程。这是否意味着多线程完全无法优化程序性能呢?由于程序运行特征千差万别,这个问题得分情况讨论。开始之前,我们先来了解两种不同的运行特征:
程序在运行时,一般都处于两种状态:
- 可执行 状态,包括 Running 以及 Ready 两种情况,这时竞争处理器资源;
- 阻塞 状态,一般为等待 IO 处理,这时让出处理器资源;
根据程序分别处于 Running 以及 IO Blocked 两种状态的时间占比,可分为两种:
- 计算密集型 ,程序执行时大部分时间处于 Running 状态;
- IO 密集型 ,程序执行时大部分时间处于 IO Blocked 状态;
多线程的网络爬虫程序可以极大缩减时间
计算密集型的话 只能使用多进程
GIL是为了方便解释器开发人员而设置的, 尤其是在内存管理方面。 假设没有GIL, 那么两个线程在销毁同一个对象的时候, 就会出现悬空指针的问题。 于是python引用了GIL, 它是以字节码为单位的, 不管何时, 只有一个线程在执行字节码。 如果当前的某条字节码没有执行完, 那么是不会发生线程切换的, 因此这样就保证了以字节码为单位的安全性。 但是我们在写python代码时, 由于一行代码一般会对应多条字节码, 所以这个时候会再通过threading.Lock()进行加锁 所以: GIL是以字节码为单位的, 它保证了每条字节码在执行的时候不会被打断 而threading.Lock()是以代码为单位的, 它是保证python层面上的一行或多行代码在执行时不会被打断
发展几十年 有一些尝试 但是仍然不成熟 官方也没有