Python 线程锁
Python 的线程锁包括互斥锁和可重入锁,同时也有条件变量、信号量等,均存在 threading 包中
- Lock:标准互斥锁,一旦被锁随后再获取锁的线程会被阻塞
- RLock:可重入锁,当前线程再次尝试获取锁时会直接成功,线程内所有获取到锁的均释放锁才释放(用的计数)
- Condition:条件变量
- Semaphore:信号量
- Event:事件对象
GIL
Python 中的锁整体是来源于 GIL 的,GIL 的目的是让多个线程按照一定的顺序并发执行,而不是简单的如互斥锁保证当下时刻只有一个线程运行。
Python 3.2 起 GIL 的线程调度策略为保证每线程运行 0.005 秒后到下一线程(之前是 100 条语句),这一时间可以通过 sys.getswitchinterval()
、sys.setswitchinterval()
获取和设置。另外需要注意的是 Python 的线程不是用户线程而是系统线程,其线程调度策略是由操作系统决定。
初始化
Python 初始化 GIL 是通过 PyEval_InitThreads
进行的(在 Python 3.7 开始相关操作已经在 Py_Initialize
中完成,而在之前会在使用线程相关功能时才调用)
如下是 Python 3.6 的 PyEval_InitThreads
源码(新版本中已经删除,位于 Python/ceval.c 文件)
1 | void |
这段代码做了
- 如果已经创建好了 GIL 什么也不做直接返回
- 创建 GIL
- 获取 GIL
创建 GIL
create_gil
函数在 Python/ceval_gil.h 文件中
1 | static void create_gil(void) |
FORCE_SWITCHING 的作用是是否强制在每个 GIL 周期结束后切换线程。简单起见,可以先认为这个不存在,然后看代码
1 | static _Py_atomic_int gil_locked = {-1}; |
这个里面做了以下几件事
- 创建了一个互斥锁 gil_mutex
- 创建了一个条件变量 gil_cond
Lock
threading.Lock ← _allocate_lock ← _thread.allocate_lock ← [_threadmodule.c] thread_PyThread_allocate_lock ← newlockobject
核心源码位于 https://github.com/python/cpython/blob/master/Modules/_threadmodule.c#L571
创建锁
1 | static lockobject * |
创建锁的过程核心只有一句 self->lock_lock = PyThread_allocate_lock();