下面给大家一个宝贵的建议,对于那些从来没有学习过编程或者并非计算机专业的编程学习者而言,Python主线程是最好的选择之一,并建议那些初学的程序员先从Python开始学习编程。
如果waitflag为0,Python会检查当前GIL是否可用,GIL中的owned是指示GIL是否可用的变量,在前面的InitializeNonRecursiveMutex中我们看到这个值被初始化为-1,Python会检查这个值是否为-1,如果是,则意味着GIL可用。
必须将其置为0,当owned为0后,表示该GIL已经被一个线程占用,不再可用。对于我们这里分析的调用PyEval_InitThread的主线程而言,由于在初始化GIL之后就调用PyThread_ acquire_lock申请GIL。
到这时,并没有第二个线程被创建,所以主线程会轻而易举地获得GIL的使用权。注意这里的检查和更新owned的操作是通过一个Win32的系统API——Interlocked- CompareExchange——来完成的。这个API是一个原子操作,其函数原形和功能如下。
与InterlockedCompareExchange相同的,InterlockedIncrement也是一个原子操作,其功能是将mutex->owned的值增加1。从这里可以看到,当一个线程开始等待GIL时,其owned就会被增加1。
显然我们可以猜测,当一个线程最终释放GIL时,一定会将GIL的owned减1,这样当所有需要GIL的线程都最终释放了GIL之后,owned会再次变为-1,意味着GIL再次变为可用。
为了清晰地展示这一点,我们现在就来看看PyThread_aquire_lock的逆运算,PyThread_release_lock每一个将从运行转态转为等待状态的线程都会在被挂起之前调用它以释放对GIL的占有。
- void PyThread_release_lock(PyThread_type_lock aLock)
- {
- LeaveNonRecursiveMutex((PNRMUTEX) aLock);
- }
- BOOL LeaveNonRecursiveMutex(PNRMUTEX mutex)
- {
- /* We don't own the mutex */
- mutex->thread_id = 0 ;
- return
- InterlockedDecrement(&mutex->owned) < 0 ||
- SetEvent(mutex->hevent) ; /* Other threads are waiting, wake one on
- them up */
- }
最终,一个线程在释放GIL时,会通过SetEvent通知所有在等待GIL的hevent这个Event内核对象的线程,结合前面的分析。如果这时候有线程在等待GIL的hevent,那么将被操作系统唤醒。这就是我们在前面介绍的Python主线程将线程调度的第二个难题委托给操作系统来实现的机制。
到了这时,调用PyEval_InitThread的线程(也就是Python主线程)已经成功获得了GIL,最后会调用PyThread_get_thread_ident(),通过Win32的API:GetCurrent- ThreadId,获得当前Python主线程的id。
【编辑推荐】