淺析Python多線程問(wèn)題
Python多線程環(huán)境的建立,說(shuō)得直白一點(diǎn),主要就是創(chuàng)建GIL。我們已經(jīng)知道GIL對(duì)于Python的多線程機(jī)制的重要意義,然而這個(gè)GIL到底是如何實(shí)現(xiàn)的呢,哎這還是個(gè)非常困擾的問(wèn)題。
- 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 */
- }
終于見識(shí)到了神秘的GIL(interpreter_lock),沒(méi)想到吧,萬(wàn)萬(wàn)沒(méi)想到,它居然指示一個(gè)簡(jiǎn)單的void*。但是轉(zhuǎn)念一想,在C中void*幾乎可以是任何東西,這家伙,可是個(gè)***容器啊。
可以看到,無(wú)論創(chuàng)建多少個(gè)線程,Python建立多線程環(huán)境的動(dòng)作只會(huì)執(zhí)行一次。在PyEval_InitThreads的開始,Python會(huì)檢查GIL是否已經(jīng)被創(chuàng)建。如果是,則不再進(jìn)行任何動(dòng)作,否則,就會(huì)創(chuàng)建這個(gè)GIL。創(chuàng)建GIL的工作由PyThread_allocate_lock完成,我們來(lái)看一看這個(gè)GIL到底是何方神圣。
在這里,我們終于看到了Python多線程機(jī)制的平臺(tái)相關(guān)性,在Python25\Python目錄下,有一大批thread這樣的文件。在這些文件中,包裝了不同操作系統(tǒng)的原生線程,并通過(guò)統(tǒng)一的接口暴露給Python,比如這里的PyThread_allocate_lock就是這樣一個(gè)接口。#t#
我們這里的thread_nt.h中包裝的是Win32平臺(tái)的原生thread,在本章中后面的代碼剖析中,還會(huì)有大量與平臺(tái)相關(guān)的代碼,我們都以Win32平臺(tái)為例。在PyThread_allocate_lock中,與PyEval_InitThreads非常類似的,它會(huì)檢查一個(gè)initialized的變量,如果說(shuō)GIL指示著Python多線程環(huán)境是否已經(jīng)建立。
那么這個(gè)initialized變量就指示著為了使用底層平臺(tái)所提供的原生thread,必須的初始化動(dòng)作是否完成。這些必須的初始化動(dòng)作通常都是底層操作系統(tǒng)所提供的API,不同的操作系統(tǒng)可能需要不同的初始化動(dòng)作。
在PyThread_allocate_lock中,出現(xiàn)了一個(gè)關(guān)鍵的結(jié)構(gòu)體PNRMUTEX,我們發(fā)現(xiàn),這個(gè)結(jié)構(gòu)體是函數(shù)的返回值,實(shí)際上也就是PyEval_InitThread中需要?jiǎng)?chuàng)建的那個(gè)interperter_lock(GIL)。原來(lái)GIL就是這個(gè)家伙,我們來(lái)看一看它的真身。
- [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中,所有的數(shù)據(jù)成員的類型都是Win32平臺(tái)下的類型風(fēng)格了,owned和thread_id都很普通,而其中的HANDLE hevent卻值得注意,我們來(lái)看看AllocNon- RecursiveMutex究竟為這個(gè)hevent準(zhǔn)備了什么。