在python的http、asyncio、urllib等模块中都会用到threading模块,所以想更深入的了解下threading模块,threading模块是基于CPython中_thread模块的Python封装。提供了常用的条件变量Condition、锁Lock、递归锁RLock、信号量Semaphore、同步事件Event、线程Thread等多个类,是比较重要的模块。
首先我们看下CPython中的_thread模块
static struct PyModuleDef threadmodule = {
PyModuleDef_HEAD_INIT,
"_thread",
thread_doc,
-1,
thread_methods,
NULL,
NULL,
NULL,
NULL
};
static PyMethodDef thread_methods[] = {
{"start_new_thread", (PyCFunction)thread_PyThread_start_new_thread,
METH_VARARGS, start_new_doc},
{"start_new", (PyCFunction)thread_PyThread_start_new_thread,
METH_VARARGS, start_new_doc},
{"allocate_lock", (PyCFunction)thread_PyThread_allocate_lock,
METH_NOARGS, allocate_doc},
{"allocate", (PyCFunction)thread_PyThread_allocate_lock,
METH_NOARGS, allocate_doc},
{"exit_thread", (PyCFunction)thread_PyThread_exit_thread,
METH_NOARGS, exit_doc},
{"exit", (PyCFunction)thread_PyThread_exit_thread,
METH_NOARGS, exit_doc},
{"interrupt_main", (PyCFunction)thread_PyThread_interrupt_main,
METH_NOARGS, interrupt_doc},
{"get_ident", (PyCFunction)thread_get_ident,
METH_NOARGS, get_ident_doc},
{"_count", (PyCFunction)thread__count,
METH_NOARGS, _count_doc},
{"stack_size", (PyCFunction)thread_stack_size,
METH_VARARGS, stack_size_doc},
{"_set_sentinel", (PyCFunction)thread__set_sentinel,
METH_NOARGS, _set_sentinel_doc},
{NULL, NULL} /* sentinel */
};
锁Lock
_allocate_lock = _thread.allocate_lock
Lock = _allocate_lock
可见Lock对象即_thread.allocate_lock在CPython中是
(PyCFunction)thread_PyThread_allocate_lock函数
static PyObject *
thread_PyThread_allocate_lock(PyObject *self)
{
return (PyObject *) newlockobject();
}
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;
}
Lock对象的元类是Locktype,而lock_lock保存实现锁操作的内核句柄,windows中lock_lock是
由CreateSemaphore(NUL,1,1,NULL)创建的系统信号量。既然Lock对象的元类是Locktype,那么我们首先来看下Locktype。
static PyTypeObject Locktype = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"_thread.lock", /*tp_name*/
sizeof(lockobject), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)lock_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_reserved*/
(reprfunc)lock_repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash*/
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
0, /*tp_doc*/
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
offsetof(lockobject, in_weakreflist), /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
lock_methods, /*tp_methods*/
};
static PyMethodDef lock_methods[] = {
{"acquire_lock", (PyCFunction)lock_PyThread_acquire_lock,
METH_VARARGS | METH_KEYWORDS, acquire_doc},
{"acquire", (PyCFunction)lock_PyThread_acquire_lock,
METH_VARARGS | METH_KEYWORDS, acquire_doc},
{"release_lock", (PyCFunction)lock_PyThread_release_lock,
METH_NOARGS, release_doc},
{"release", (PyCFunction)lock_PyThread_release_lock,
METH_NOARGS, release_doc},
{"locked_lock", (PyCFunction)lock_locked_lock,
METH_NOARGS, locked_doc},
{"locked", (PyCFunction)lock_locked_lock,
METH_NOARGS, locked_doc},
{"__enter__", (PyCFunction)lock_PyThread_acquire_lock,
METH_VARARGS | METH_KEYWORDS, acquire_doc},
{"__exit__", (PyCFunction)lock_PyThread_release_lock,
METH_VARARGS, release_doc},
{NULL, NULL} /* sentinel */
};
Lock对象支持的方法都在这里一览无余。
Lock()实例时会创建lockobject对象 同时 CreateSemaphore(NUL,1,1,NULL)创建信号量,保存句柄到lock_lock中。再来看看Lock上锁acquire和解锁release方法。
- 上锁acquire
acquire方法即lock_PyThread_acquire_lock
static PyObject *
lock_PyThread_acquire_lock(lockobject *self, PyObject *args, PyObject *kwds)
{
_PyTime_t timeout;
PyLockStatus r;
if (lock_acquire_parse_args(args, kwds, &timeout) < 0)
return NULL;
r = acquire_timed(self->lock_lock, timeout);
if (r == PY_LOCK_INTR) {
return NULL;
}
if (r == PY_LOCK_ACQUIRED)
self->locked = 1;
return PyBool_FromLong(r == PY_LOCK_ACQUIRED);
}
将lock_lock内核句柄传入,会调用
WaitForSingleObjectEx(lock_lock,timeout,FLASE)
解锁就更简单了
- 释放锁release
lock_PyThread_release_lock调用
ReleaseSemaphore(lock_lock,1,NULL)
由此可见,threading模块的Lock对象在CPython层面简单的封装了
CreateSemaphore(NUL,1,1,NULL)
WaitForSingleObjectEx(lock_lock,timeout,FLASE)
ReleaseSemaphore(lock_lock,1,NULL)
实现了上锁,等锁,解锁等方法。
但CPython未实现Condition、Event、Semaphore等同步互斥方式,这些都由Python实现。
而Event和Semaphore又是基于Condition实现的。所以后面我们首先分析下Condition条件变量。