六,python的线程机制
GIL锁的机制,来源于python的内存管理和为了实现多线程,对共享内存资源的互斥实现。
当然,python对进程的支持很好,这在linux下,很有比线程更好的使用,因为在linux里没有线程的概念,
有着的是轻量级的进程以及pipeline等进程间通信。
如果非要使用线程,解释器只有一个,导致的各种线程必须要获得字节码解释器,也就是GIL。
有两个核心问题:在何时挂起当前线程,选择下一个线程?在众多等待的线程中选择其中一个?
对于第一个问题,python通过执行的字节码指令弄的,如下:
import sys
sys.getcheckinterval()
Out[2]: 100
100条指令便会切换下一个。
至于第二个问题:由底层的操作系统决定,因为python的线程实际上是将obj这些直接调用的Event指令。
在thread模块中,有如下的方法:
static PyMethodDef lock_methods[] = {
{"acquire_lock", (PyCFunction)lock_PyThread_acquire_lock,
METH_VARARGS, acquire_doc},
{"acquire", (PyCFunction)lock_PyThread_acquire_lock,
METH_VARARGS, 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, acquire_doc},
{"__exit__", (PyCFunction)lock_PyThread_release_lock,
METH_VARARGS, release_doc},
{NULL} /* sentinel */
};
想必你也注意到了,有很多重复的。
线程的创建:
static PyObject *
thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs)
{
PyObject *func, *args, *keyw = NULL;
struct bootstate *boot;
long ident;
if (!PyArg_UnpackTuple(fargs, "start_new_thread", 2, 3,
&func, &args, &keyw))
return NULL;
if (!PyCallable_Check(func)) {
PyErr_SetString(PyExc_TypeError,
"first arg must be callable");
return NULL;
}
if (!PyTuple_Check(args)) {
PyErr_SetString(PyExc_TypeError,
"2nd arg must be a tuple");
return NULL;
}
if (keyw != NULL && !PyDict_Check(keyw)) {
PyErr_SetString(PyExc_TypeError,
"optional 3rd arg must be a dictionary");
return NULL;
}
boot = PyMem_NEW(struct bootstate, 1);
if (boot == NULL)
return PyErr_NoMemory();
boot->interp = PyThreadState_GET()->interp;
boot->func = func;
boot->args = args;
boot->keyw = keyw;
boot->tstate = _PyThreadState_Prealloc(boot->interp);
if (boot->tstate == NULL) {
PyMem_DEL(boot);
return PyErr_NoMemory();
}
Py_INCREF(func);
Py_INCREF(args);
Py_XINCREF(keyw);
PyEval_InitThreads(); /* Start the interpreter's thread-awareness */
ident = PyThread_start_new_thread(t_bootstrap, (void*) boot);
if (ident == -1) {
PyErr_SetString(ThreadError, "can't start new thread");
Py_DECREF(func);
Py_DECREF(args);
Py_XDECREF(keyw);
PyThreadState_Clear(boot->tstate);
PyMem_DEL(boot);
return NULL;
}
return PyInt_FromLong(ident);
}
检查参数,创建并初始化bootstate结构的boot,这里保存着线程的各种信息。
初始化线程
以boot为参数创建操作系统的原生参数
好了,想必有很多疑问吧,看看GIL在python里到底怎么实现的吧。
static PyThread_type_lock interpreter_lock = 0; /* This is the GIL */
void
PyEval_InitThreads(void)
{
if (interpreter_lock)
return;
interpreter_lock = PyThread_allocate_lock();
PyThread_acquire_lock(interpreter_lock, 1);
main_thread = PyThread_get_thread_ident();
}
如下所示:这个是具有平台相关性的,在python文件夹里面放了各种thread_*.h
PyThread_allocate_lock(void)
{
PNRMUTEX aLock;
if (!initialized)
PyThread_init_thread();
aLock = AllocNonRecursiveMutex() ;
return (PyThread_type_lock) aLock;
}
typedef struct NRMUTEX {
LONG owned ;
DWORD thread_id ;
HANDLE hevent ;
} NRMUTEX, *PNRMUTEX ;
BOOL
InitializeNonRecursiveMutex(PNRMUTEX mutex)
{
mutex->owned = -1 ; /* No threads have entered NonRecursiveMutex */
mutex->thread_id = 0 ;
mutex->hevent = CreateEvent(NULL, FALSE, FALSE, NULL) ;
return mutex->hevent != NULL ; /* TRUE if the mutex is created */
}
从这里可以看出这个是原生的windows系统的Event,thread_id记录着启动的线程
接下来获取lock
int
PyThread_acquire_lock(PyThread_type_lock aLock, int waitflag)
{
int success ;
dprintf(("%ld: PyThread_acquire_lock(%p, %d) called\n", PyThread_get_thread_ident(),aLock, waitflag));
success = aLock && EnterNonRecursiveMutex((PNRMUTEX) aLock, (waitflag ? INFINITE : 0)) == WAIT_OBJECT_0 ;
dprintf(("%ld: PyThread_acquire_lock(%p, %d) -> %d\n", PyThread_get_thread_ident(),aLock, waitflag, success));
return success;
}
void
PyThread_release_lock(PyThread_type_lock aLock)
{
dprintf(("%ld: PyThread_release_lock(%p) called\n", PyThread_get_thread_ident(),aLock));
if (!(aLock && LeaveNonRecursiveMutex((PNRMUTEX) aLock)))
dprintf(("%ld: Could not PyThread_release_lock(%p) error: %ld\n", PyThread_get_thread_ident(), aLock, GetLastError()));
}
通过参数waitflag来判别,是否是等待的
通过之前的owned判别是否获得GIL