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 2 3 4 5 6 7 8 9 10 11
| void PyEval_InitThreads(void) { if (gil_created()) return; create_gil(); take_gil(PyThreadState_GET()); main_thread = PyThread_get_thread_ident(); if (!pending_lock) pending_lock = PyThread_allocate_lock(); }
|
这段代码做了
- 如果已经创建好了 GIL 什么也不做直接返回
- 创建 GIL
- 获取 GIL
创建 GIL
create_gil 函数在 Python/ceval_gil.h 文件中
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| static void create_gil(void) { MUTEX_INIT(gil_mutex); #ifdef FORCE_SWITCHING MUTEX_INIT(switch_mutex); #endif COND_INIT(gil_cond); #ifdef FORCE_SWITCHING COND_INIT(switch_cond); #endif _Py_atomic_store_relaxed(&gil_last_holder, 0); _Py_ANNOTATE_RWLOCK_CREATE(&gil_locked); _Py_atomic_store_explicit(&gil_locked, 0, _Py_memory_order_release); }
|
FORCE_SWITCHING 的作用是是否强制在每个 GIL 周期结束后切换线程。简单起见,可以先认为这个不存在,然后看代码
1 2 3 4 5 6 7 8 9 10 11 12
| static _Py_atomic_int gil_locked = {-1}; static COND_T gil_cond; static MUTEX_T gil_mutex;
static void create_gil(void) { MUTEX_INIT(gil_mutex); COND_INIT(gil_cond); _Py_atomic_store_relaxed(&gil_last_holder, 0); _Py_ANNOTATE_RWLOCK_CREATE(&gil_locked); _Py_atomic_store_explicit(&gil_locked, 0, _Py_memory_order_release); }
|
这个里面做了以下几件事
- 创建了一个互斥锁 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| static lockobject * newlockobject(void) { lockobject *self; self = PyObject_New(lockobject, &Locktype); if (self == NULL) return NULL; self->lock_lock = PyThread_allocate_lock(); self->locked = 0; self->in_weakreflist = NULL; if (self->lock_lock == NULL) { Py_DECREF(self); PyErr_SetString(ThreadError, "can't allocate lock"); return NULL; } return self; }
|
创建锁的过程核心只有一句 self->lock_lock = PyThread_allocate_lock();
参考资料
Python的锁源码剖析
Python进阶:深入GIL(下篇)
GIL 究竟是什么