在Python里如果你建立一个程序,就是一个进程,其中包含一个线程,这个就是主线程,而是为了提高资源使用效率来提高系统的效率,希望大家能够再次学到自己想要的信息。
从这里可以看到,当一个线程开始等待GIL时,其owned就会被增加1。显然我们可以猜测,当一个线程最终释放GIL时,一定会将GIL的owned减1,这样当所有需要GIL的线程都最终释放了GIL之后,owned会再次变为-1,意味着GIL再次变为可用。
为了清晰地展示这一点,我们现在就来看看PyThread_aquire_lock的逆运算,PyThread_release_lock每一个将从运行转态转为等待状态的线程都会在被挂起之前调用它以释放对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 */
- }
最终,一个线程在释放GIL时,会通过SetEvent通知所有在等待GIL的hevent这个Event内核对象的线程,结合前面的分析,如果这时候有线程在等待GIL的hevent,那么将被操作系统唤醒。
这就是我们在前面介绍的Python将线程调度的第二个难题委托给操作系统来实现的机制。到了这时,调用PyEval_InitThread的线程(也就是Python主线程)已经成功获得了GIL。***会调用PyThread_get_thread_ident()。
通过Win32的API:GetCurrent- ThreadId,获得当前Python主线程的id,并将其赋给main_thread,main_thread是一个静态全局变量,专职存储Python主线程的线程id,用以判断一个线程是否是Python主线程。
在完成了多线程环境的初始化之后,Python会开始创建底层平台的原生thread,以thread1.py为例,这个原生thread将执行threadProc所定义的操作。从现在开始,为了描述的清晰性,我们将Python主线程,也就是调用thread_PyThread_start_new_thread创建新的线程的线程称为主线程,而将与threadProc对应的原生thread称之为子线程。现在我们来看看一个子线程是如何被创建的。
- static PyObject* thread_PyThread_start_new_thread(PyObject *self, PyObject
- *fargs)
- {
- PyObject *func, *args, *keyw = NULL;
- struct bootstate *boot;
- long ident;
- PyArg_UnpackTuple(fargs, "start_new_thread", 2, 3, &func, &args, &keyw);
- //[1]:创建bootstate结构
- boot = PyMem_NEW(struct bootstate, 1);
- boot->interp = PyThreadState_GET()->interp;
- boot->funcfunc = func;
- boot->argsargs = args;
- boot->keywkeyw = keyw;
- //[2]:初始化多线程环境
- PyEval_InitThreads(); /* Start the interpreter's thread-awareness */
- //[3]:创建线程
- ident = PyThread_start_new_thread(t_bootstrap, (void*) boot);
- return PyInt_FromLong(ident);
- [thread.c]
- /* Support for runtime thread stack size tuning.
- A value of 0 means using the platform's default stack size
- or the size specified by the THREAD_STACK_SIZE macro. */
- static size_t _pythread_stacksize = 0;
- [thread_nt.h]
- long PyThread_start_new_thread(void (*func)(void *), void *arg)
- {
Python主线程通过调用PyThread_start_new_thread完成创建子线程的工作。为了清晰地理解PyThread_start_new_thread的工作,我们需要特别注意该函数的参数。从thread_ PyThread_start_new_thread中可以看到,这里的func实际上是函数t_bootstrap,而arg则是在thread_PyThread_start_new_thread中创建的bootstate结构体boot。在boot中,保存着Python程序(thread1.py)中所定义的线程的信息。
【编辑推荐】