最近阅读《Python源码剖析》对进程线程的封装解释:

GIL,Global Interpreter Lock,对于python的多线程机制非常重要,其如何实现的?代码中实现如下:

指向一个void*,C语言中的空指针类型可以指向任意类型。Python建立多线程环境的动作只会执行一次。

PyEval_InitThreads--》PyThread_allocate_lock创建GIL之后,当前线程开始遵守python的多线程机制,即任何调用Python C API之前需要先获得GIL.

也就是代码中PyThread_acquire_lock尝试获取GIL。

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},
{NULL, NULL} /* sentinel */
}; /*创建bootstate,并初始化,其保存关于线程的一切信息,如线程过程,和参数等,*/
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", , ,
&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, );
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 == -) {
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);
} /*以boot为参数,创建一个原生线程*/
PyThreadState *
_PyThreadState_Prealloc(PyInterpreterState *interp)
{
return new_threadstate(interp, );
} static PyThreadState *
new_threadstate(PyInterpreterState *interp, int init)
{
PyThreadState *tstate = (PyThreadState *)malloc(sizeof(PyThreadState)); if (_PyThreadState_GetFrame == NULL)
_PyThreadState_GetFrame = threadstate_getframe; if (tstate != NULL) {
tstate->interp = interp; tstate->frame = NULL;
tstate->recursion_depth = ;
tstate->tracing = ;
tstate->use_tracing = ;
tstate->tick_counter = ;
tstate->gilstate_counter = ;
tstate->async_exc = NULL;
#ifdef WITH_THREAD
tstate->thread_id = PyThread_get_thread_ident();
#else
tstate->thread_id = ;
#endif tstate->dict = NULL; tstate->curexc_type = NULL;
tstate->curexc_value = NULL;
tstate->curexc_traceback = NULL; tstate->exc_type = NULL;
tstate->exc_value = NULL;
tstate->exc_traceback = NULL; tstate->c_profilefunc = NULL;
tstate->c_tracefunc = NULL;
tstate->c_profileobj = NULL;
tstate->c_traceobj = NULL; tstate->trash_delete_nesting = ;
tstate->trash_delete_later = NULL; if (init)
_PyThreadState_Init(tstate); HEAD_LOCK();
tstate->next = interp->tstate_head;
interp->tstate_head = tstate;
HEAD_UNLOCK();
} return tstate;
}

GIL(NRMUTEX)对象,结构中有4个成员,其中hevent就是Win32平台下的Event内核对象,而thread_id则记录任意时刻获取的GIL的线程ID。

 /*
* Lock support. It has too be implemented as semaphores.
* I [Dag] tried to implement it with mutex but I could find a way to
* tell whether a thread already own the lock or not.
*/
PyThread_type_lock
PyThread_allocate_lock(void)
{
PNRMUTEX aLock; dprintf(("PyThread_allocate_lock called\n"));
if (!initialized)
PyThread_init_thread(); aLock = AllocNonRecursiveMutex() ; dprintf(("%ld: PyThread_allocate_lock() -> %p\n", PyThread_get_thread_ident(), aLock)); return (PyThread_type_lock) aLock;
} typedef struct NRMUTEX {
LONG owned ;
DWORD thread_id ;
HANDLE hevent ;
} NRMUTEX, *PNRMUTEX ; PNRMUTEX
AllocNonRecursiveMutex(void)
{
PNRMUTEX mutex = (PNRMUTEX)malloc(sizeof(NRMUTEX)) ;
if (mutex && !InitializeNonRecursiveMutex(mutex))
{
free(mutex) ;
mutex = NULL ;
}
return mutex ;
} BOOL
InitializeNonRecursiveMutex(PNRMUTEX mutex)
{
mutex->owned = - ; /* No threads have entered NonRecursiveMutex */
mutex->thread_id = ;
mutex->hevent = CreateEvent(NULL, FALSE, FALSE, NULL) ;
return mutex->hevent != NULL ; /* TRUE if the mutex is created */
}

PyThread_acquire_lock尝试获取GIL代码如下:

void
PyEval_InitThreads(void)
{
if (interpreter_lock)
return;
interpreter_lock = PyThread_allocate_lock();
PyThread_acquire_lock(interpreter_lock, );
main_thread = PyThread_get_thread_ident();
}
/*
* Return 1 on success if the lock was acquired
*
* and 0 if the lock was not acquired. This means a 0 is returned
* if the lock has already been acquired by this thread!
*/
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 : )) == WAIT_OBJECT_0 ; dprintf(("%ld: PyThread_acquire_lock(%p, %d) -> %d\n", PyThread_get_thread_ident(),aLock, waitflag, success)); return success;
}

Windown下调用系统的WaitForSingleObject

DWORD
EnterNonRecursiveMutex(PNRMUTEX mutex, BOOL wait)
{
/* Assume that the thread waits successfully */
DWORD ret ; /* InterlockedIncrement(&mutex->owned) == 0 means that no thread currently owns the mutex */
if (!wait)
{
if (InterlockedCompareExchange(&mutex->owned, , -) != -)
return WAIT_TIMEOUT ;
ret = WAIT_OBJECT_0 ;
}
else
ret = InterlockedIncrement(&mutex->owned) ?
/* Some thread owns the mutex, let's wait... */
WaitForSingleObject(mutex->hevent, INFINITE) : WAIT_OBJECT_0 ; mutex->thread_id = GetCurrentThreadId() ; /* We own it */
return ret ;
}

Linux下则使用互斥锁metux和lock机制,条件等待机制一起使用。

先由本线程调用status = pthread_mutex_lock( &thelock->mut )锁住,mutex保持锁定状态,并在线程挂起进入等待前解锁。

然后status = pthread_cond_wait(&thelock->lock_released,&thelock->mut);

之后status = pthread_mutex_unlock( &thelock->mut );

条件满足从而离开pthread_cond_wait()之前,mutex加锁,以加锁动作对应。
激发条件有两种形式,pthread_cond_signal()激活一个等待该条件的线程,存在多个等待线程时按入队顺序激活其中一个;而pthread_cond_broadcast()则激活所有等待线程。
pthread_cond_wait解释:
int
PyThread_acquire_lock(PyThread_type_lock lock, int waitflag)
{
int success;
pthread_lock *thelock = (pthread_lock *)lock;
int status, error = ; dprintf(("PyThread_acquire_lock(%p, %d) called\n", lock, waitflag)); status = pthread_mutex_lock( &thelock->mut );
CHECK_STATUS("pthread_mutex_lock[1]");
success = thelock->locked == ; if ( !success && waitflag ) {
/* continue trying until we get the lock */ /* mut must be locked by me -- part of the condition
* protocol */
while ( thelock->locked ) {
status = pthread_cond_wait(&thelock->lock_released,
&thelock->mut);
CHECK_STATUS("pthread_cond_wait");
}
success = ;
}
if (success) thelock->locked = ;
status = pthread_mutex_unlock( &thelock->mut );
CHECK_STATUS("pthread_mutex_unlock[1]"); if (error) success = ;
dprintf(("PyThread_acquire_lock(%p, %d) -> %d\n", lock, waitflag, success));
return success;
}

python创建子线程过程:

多线程环境初始化之后,python开始创建底层平台的原生线程。主线程通过调用 thread_PyThread_start_new_thread-》PyThread_start_new_thread完成子线程的工作,返回子线程的ID。子线程的ID只有被激活才能从子线程中获取,因此主线程等待这个子线程的ID,一旦子线程设置好ID,就会设法唤醒主线程。至此,主线程和子线程开始分道扬镳。主线程在返回子线程ID之后,继续执行后续的字节码。

PyThread_start_new_thread传入的func是函数t_bootstrap,而arg则是bootstate结构体boot。而boot中保存着程序中所定义的线程信息。PyThread_start_new_thread首先将func和arg都打包到一个类型为callobj结构体中。

创建好子线程之后,其开始与主线程对GIL竞争。在t_bootstrap中调用PyEval_AcquireThread申请GIL,成功之后就申请到GIL,接下来子线程调用PyEval_CallObjectWithKeywords并最终调用我们熟悉的函数PyEval_EvalFrameEx,也就是python的字节码执行引擎。之后执行完毕,进行清理扫尾工作PyThreadState_DeleteCurrent释放GIL。

    t_bootstrap 看上去似乎子线程一直执行到释放GIL,他们是如何激活多线程机制的呢?答案在于函数PyEval_EvalFrameEx中,python内部维护的模拟中断时钟不断激活线程的调度机制,从而实现子线程和主线程的切换。

执行秩序: thread_PyThread_start_new_thread-》PyThread_start_new_thread-》bootstrap--》t_bootstrap  

t_bootstrap 代码:

static void
t_bootstrap(void *boot_raw)
{
struct bootstate *boot = (struct bootstate *) boot_raw;
PyThreadState *tstate;
PyObject *res; tstate = boot->tstate;
tstate->thread_id = PyThread_get_thread_ident();
_PyThreadState_Init(tstate);
PyEval_AcquireThread(tstate);
nb_threads++;
res = PyEval_CallObjectWithKeywords(
boot->func, boot->args, boot->keyw);
if (res == NULL) {
if (PyErr_ExceptionMatches(PyExc_SystemExit))
PyErr_Clear();
else {
PyObject *file;
PyObject *exc, *value, *tb;
PyErr_Fetch(&exc, &value, &tb);
PySys_WriteStderr(
"Unhandled exception in thread started by ");
file = PySys_GetObject("stderr");
if (file)
PyFile_WriteObject(boot->func, file, 0);
else
PyObject_Print(boot->func, stderr, 0);
PySys_WriteStderr("\n");
PyErr_Restore(exc, value, tb);
PyErr_PrintEx(0);
}
}
else
Py_DECREF(res);
Py_DECREF(boot->func);
Py_DECREF(boot->args);
Py_XDECREF(boot->keyw);
PyMem_DEL(boot_raw);
nb_threads--;
PyThreadState_Clear(tstate);
PyThreadState_DeleteCurrent();
PyThread_exit_thread();
}

完成打包之后,调用Win32下的创建thread API 函数CreateThread或者_beginthreadex ,然后通过bootstrap调用我们定义的函数(例如自己的test.py中的def testThread 函数)

函数打包,调用代码:

Python GIL 多线程机制 (C source code)的更多相关文章

  1. python多线程机制

    Python中的线程从一开始就是操作系统的原生线程.而Python虚拟机也同样使用一个全局解释器锁(Global Interpreter Lock,GIL)来互斥线程多Python虚拟机的使用. GI ...

  2. convert source code files to pdf format in python

    import os import sys def find_file(root_dir, type): dirs_pool = [root_dir] dest_pool = [] def scan_d ...

  3. python GIL 全局锁,多核cpu下的多线程性能究竟如何?

    python GIL 全局锁,多核cpu下的多线程性能究竟如何?GIL全称Global Interpreter Lock GIL是什么? 首先需要明确的一点是GIL并不是Python的特性,它是在实现 ...

  4. Defining Python Source Code Encodings

    Defining the Encoding Python will default to ASCII as standard encoding if no other encoding hints a ...

  5. [ Python - 11 ] 多线程及GIL全局锁

    1. GIL是什么? 首先需要明确的一点是GIL并不是python的特性, 它是在实现python解析器(Cpython)时所引入的一个概念. 而Cpython是大部分环境下默认的python执行环境 ...

  6. UI5 Source code map机制的细节介绍

    在我的博客A debugging issue caused by source code mapping里我介绍了在我做SAP C4C开发时遇到的一个曾经困扰我很久的问题,最后结论是这个问题由于Jav ...

  7. Source Code Structure - Python 源码目录结构

    Source Code Structure - Python 源码目录结构 Include 目录包含了 Python 提供的所有头文件, 如果用户需要用 C 或 C++ 编写自定义模块扩展 Pytho ...

  8. Python使用import导入模块时报ValueError: source code string cannot contain null bytes的解决方案

    老猿在导入一个Python模块时报错: >>> import restartnet.py Traceback (most recent call last): File " ...

  9. Python GIL(Global Interpreter Lock)

    一,介绍 定义: In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native t ...

随机推荐

  1. java比较两个对象是否相等的方法

    java比较两个对象是否相等直接使用equals方法进行判断肯定是不会相同的. 例如: Person  person1  =new Person("张三"); Person  pe ...

  2. istringstream的操作

    今天在stackoverflow上看到这么个问题,写完之后看了看别人的提交的答案,感觉自己的答案虽然能得出正确结果但是有点啰嗦,对于c++还是没有熟练,没有想起有istringstream,而且提问的 ...

  3. Andriod如何更改应用程序小图标

    1.之前我们安装的第一个应用图标是这样的(如下图) 2.在eclipse左侧项目中找到res文件下的drawable-hdpi         3.把自己找的LOGO图标拖到文件中,之后会弹出一个消息 ...

  4. Python爬虫:Xpath语法笔记

    一.选取节点 常用的路劲表达式: 表达式 描述 实例   nodename 选取nodename节点的所有子节点 xpath(‘//div’) 选取了div节点的所有子节点 / 从根节点选取 xpat ...

  5. Android课程---如何用网格视图做出手机桌面APP

    activity_test.xml <?xml version="1.0" encoding="utf-8"?> <GridView xmln ...

  6. C#枚举类型

    枚举是一个指定的常数,其基础类型可以是除 Char 外的任何整型.如果没有显式声明基础类型,则使用 Int32. 定义 默认基数从0开始,也可指定数值. enum Days { Saturday, / ...

  7. NV SDK 10 (1) Clipmaps

    Clipmaps sample: Abstract Clipmaps are a feature first implemented on SGI workstations that allow ma ...

  8. MVC第二天

    一.过滤器filter 注意:如果继承自接口需要让类实现FilterAttribute,才可以作为特性使用使用方式1:作为Controller或Action的特性使用方式2:在Global中注册为全局 ...

  9. Software Solutions CACHE COHERENCE AND THE MESI PROTOCOL

    COMPUTER ORGANIZATION AND ARCHITECTURE DESIGNING FOR PERFORMANCE NINTH EDITION Software cache cohere ...

  10. Pycharm Professional Edition 激活码(license)

    http://blog.csdn.net/yangysc/article/details/52355865 43B4A73YYJ-eyJsaWNlbnNlSWQiOiI0M0I0QTczWVlKIiw ...