Python多线程环境的建立,说得直白一点,主要就是创建GIL。我们已经知道GIL对于Python的多线程机制的重要意义,然而这个GIL到底是如何实现的呢,哎这还是个非常困扰的问题。
- 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 = -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 */
- }
终于见识到了神秘的GIL(interpreter_lock),没想到吧,万万没想到,它居然指示一个简单的void*。但是转念一想,在C中void*几乎可以是任何东西,这家伙,可是个***容器啊。
可以看到,无论创建多少个线程,Python建立多线程环境的动作只会执行一次。在PyEval_InitThreads的开始,Python会检查GIL是否已经被创建。如果是,则不再进行任何动作,否则,就会创建这个GIL。创建GIL的工作由PyThread_allocate_lock完成,我们来看一看这个GIL到底是何方神圣。
在这里,我们终于看到了Python多线程机制的平台相关性,在Python25\Python目录下,有一大批thread这样的文件。在这些文件中,包装了不同操作系统的原生线程,并通过统一的接口暴露给Python,比如这里的PyThread_allocate_lock就是这样一个接口。#t#
我们这里的thread_nt.h中包装的是Win32平台的原生thread,在本章中后面的代码剖析中,还会有大量与平台相关的代码,我们都以Win32平台为例。在PyThread_allocate_lock中,与PyEval_InitThreads非常类似的,它会检查一个initialized的变量,如果说GIL指示着Python多线程环境是否已经建立。
那么这个initialized变量就指示着为了使用底层平台所提供的原生thread,必须的初始化动作是否完成。这些必须的初始化动作通常都是底层操作系统所提供的API,不同的操作系统可能需要不同的初始化动作。
在PyThread_allocate_lock中,出现了一个关键的结构体PNRMUTEX,我们发现,这个结构体是函数的返回值,实际上也就是PyEval_InitThread中需要创建的那个interperter_lock(GIL)。原来GIL就是这个家伙,我们来看一看它的真身。
- [thread_nt.h]
- 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 = -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 */
- }
在NRMUTEX中,所有的数据成员的类型都是Win32平台下的类型风格了,owned和thread_id都很普通,而其中的HANDLE hevent却值得注意,我们来看看AllocNon- RecursiveMutex究竟为这个hevent准备了什么。