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
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 究竟是什么