成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

如何正確區(qū)分Python線程

開(kāi)發(fā) 后端
怎么區(qū)分Python線程是屬于哪個(gè)狀態(tài)對(duì)象呢,幸好我們還有線程id呢,ID存儲(chǔ)的正是各個(gè)線程的id,根據(jù)這個(gè)有效ID,可以區(qū)分不同的Python線程。

在Python語(yǔ)言中Python線程可以從這里開(kāi)始與主線程對(duì)GIL的競(jìng)爭(zhēng),在t_bootstrap中,申請(qǐng)完了GIL,也就是說(shuō)子線程也就獲得了GIL,使其始終保存著活動(dòng)線程的狀態(tài)對(duì)象。

當(dāng)PyEval_AcquireThread結(jié)束之后,子線程也就獲得了GIL,并且做好了一切執(zhí)行的準(zhǔn)備。接下來(lái)子線程通過(guò)PyEval_ CallObjectWithKeywords,將最終調(diào)用我們已經(jīng)非常熟悉的PyEval_EvalFrameEx。

也就是Python的字節(jié)碼執(zhí)行引擎。傳遞進(jìn)PyEval_CallObjectWithKeywords的boot->func是一PyFunctionObject對(duì)象,正是therad1.py中定義的threadProc編譯后的結(jié)果。在PyEval_CallObjectWithKeywords結(jié)束之后,子線程將釋放GIL,并完成銷毀線程的所有掃尾工作,到了這里,子線程就結(jié)束了。

從t_bootstrap的代碼看上去,似乎子線程會(huì)一直執(zhí)行,直到子線程的所有計(jì)算都完成,才會(huì)通過(guò)PyThreadState_DeleteCurrent釋放GIL。如此一來(lái),那主線程豈非一直都會(huì)處于等待GIL的狀態(tài)?如果真是這樣,那Python線程顯然就不可能支持多線程機(jī)制了。

實(shí)際上在PyEval_EvalFrameEx中,圖15-2中顯示的Python內(nèi)部維護(hù)的那個(gè)模擬時(shí)鐘中斷會(huì)不斷地激活線程的調(diào)度機(jī)制,在子線程和主線程之間不斷地進(jìn)行切換。從而真正實(shí)現(xiàn)多線程機(jī)制,當(dāng)然,這一點(diǎn)我們將在后面詳細(xì)剖析。現(xiàn)在我們感興趣的是子線程在PyEval_AcquireThreade中到底做了什么。

到這里,了解了PyEval_AcquireThread,似乎創(chuàng)建線程的機(jī)制都清晰了。但實(shí)際上,有一個(gè)非常重要的機(jī)制——線程狀態(tài)保護(hù)機(jī)制——隱藏在了一個(gè)毫不起眼的地方:PyThreadState_New。

  1. [threadmodule.c]  
  2.  
  3. static PyObject* thread_PyThread_start_new_thread(PyObject *self, PyObject  
  4.  
  5.   *fargs)  
  6.  
  7. {  
  8.  
  9.     PyObject *func, *args, *keyw = NULL;  
  10.  
  11.     struct bootstate *boot;  
  12.  
  13.     long ident;  
  14.  
  15.     PyArg_UnpackTuple(fargs, "start_new_thread", 2, 3, &func, &args, &keyw);  
  16.  
  17.     //[1]:創(chuàng)建bootstate結(jié)構(gòu)  
  18.  
  19.     boot = PyMem_NEW(struct bootstate, 1);  
  20.  
  21.     boot->interp = PyThreadState_GET()->interp;  
  22.  
  23.     boot->funcfunc = func;  
  24.  
  25.     boot->argsargs = args;  
  26.  
  27.     boot->keywkeyw = keyw;  
  28.  
  29.     //[2]:初始化多線程環(huán)境  
  30.  
  31.     PyEval_InitThreads(); /* Start the interpreter's thread-awareness */  
  32.  
  33.     //[3]:創(chuàng)建線程  
  34.  
  35.     ident = PyThread_start_new_thread(t_bootstrap, (void*) boot);  
  36.  
  37.     return PyInt_FromLong(ident);  
  38.  
  39. [thread.c]  
  40.  
  41. /* Support for runtime thread stack size tuning.  
  42.  
  43.    A value of 0 means using the platform's default stack size  
  44.  
  45.    or the size specified by the THREAD_STACK_SIZE macro. */  
  46.  
  47. static size_t _pythread_stacksize = 0;  
  48.  
  49. [thread_nt.h]  
  50.  
  51. long PyThread_start_new_thread(void (*func)(void *), void *arg)  
  52.  
  53. {  
  54.  
  55.     unsigned long rv;  
  56.  
  57.     callobj obj;  
  58.  
  59.     obj.id = -1;    /* guilty until proved innocent */  
  60.  
  61.     obj.func = func;  
  62.  
  63.     obj.arg = arg;  
  64.  
  65.     obj.done = CreateSemaphore(NULL, 0, 1, NULL);  
  66.  
  67.     rv = _beginthread(bootstrap, _pythread_stacksize, &obj); /* use default stack size */  
  68.  
  69.     if (rv == (unsigned long)-1) {  
  70.  
  71.         //創(chuàng)建raw thread失敗  
  72.  
  73.         obj.id = -1;  
  74.  
  75.     }  
  76.  
  77.     else {  
  78.  
  79.         WaitForSingleObject(obj.done, INFINITE);  
  80.  
  81.     }  
  82.  
  83.     CloseHandle((HANDLE)obj.done);  
  84.  
  85.     return obj.id;  
  86.  
  87. }  

這個(gè)機(jī)制對(duì)于理解Python線程的創(chuàng)建和維護(hù)是非常關(guān)鍵的。要剖析線程狀態(tài)的保護(hù)機(jī)制,我們首先需要回顧一下線程狀態(tài)。在Python中,每一個(gè)Python線程都會(huì)有一個(gè)線程狀態(tài)對(duì)象與之關(guān)聯(lián)。

在線程狀態(tài)對(duì)象中,記錄了每一個(gè)線程所獨(dú)有的一些信息。實(shí)際上,在剖析Python的初始化過(guò)程時(shí),我們?cè)?jīng)見(jiàn)過(guò)這個(gè)對(duì)象。每一個(gè)線程對(duì)應(yīng)的線程狀態(tài)對(duì)象都保存著這個(gè)線程當(dāng)前的PyFrameObject對(duì)象,線程的id這樣一些信息。有時(shí)候,線程是需要訪問(wèn)這些信息的。

比如考慮一個(gè)最簡(jiǎn)單的情形,在某種情況下,每個(gè)線程都需要訪問(wèn)線程狀態(tài)對(duì)象中所保存的thread_id信息,顯然,線程A獲得的應(yīng)該是A的thread_id,線程B亦然。倘若線程A獲得的是B的thread_id,那就壞菜了。這就意味著Python線程內(nèi)部必須有一套機(jī)制,這套機(jī)制與操作系統(tǒng)管理進(jìn)程的機(jī)制非常類似。

我們知道,在操作系統(tǒng)從進(jìn)程A切換到進(jìn)程B時(shí),首先會(huì)保存進(jìn)程A的上下文環(huán)境,再進(jìn)行切換;當(dāng)從進(jìn)程B切換回進(jìn)程A時(shí),又會(huì)恢復(fù)進(jìn)程A的上下文環(huán)境,這樣就保證了進(jìn)程A始終是在屬于自己的上下文環(huán)境中運(yùn)行。

這里的線程狀態(tài)對(duì)象就等同于進(jìn)程的上下文,Python同樣會(huì)有一套存儲(chǔ)、恢復(fù)線程狀態(tài)對(duì)象的機(jī)制。同時(shí),在Python內(nèi)部,維護(hù)著一個(gè)全局變量:PyThreadState * _PyThread- State_Current。

當(dāng)前活動(dòng)線程所對(duì)應(yīng)的線程狀態(tài)對(duì)象就保存在這個(gè)變量里,當(dāng)Python調(diào)度線程時(shí),會(huì)將被激活的線程所對(duì)應(yīng)的線程狀態(tài)對(duì)象賦給_PyThreadState_Current,使其始終保存著活動(dòng)線程的狀態(tài)對(duì)象。

這就引出了這樣的一個(gè)問(wèn)題:Python如何在調(diào)度進(jìn)程時(shí),獲得被激活線程對(duì)應(yīng)的狀態(tài)對(duì)象?Python內(nèi)部會(huì)通過(guò)一個(gè)單向鏈表來(lái)管理所有的Python線程的狀態(tài)對(duì)象,當(dāng)需要尋找一個(gè)線程對(duì)應(yīng)的狀態(tài)對(duì)象時(shí)。#t#

就遍歷這個(gè)鏈表,搜索其對(duì)應(yīng)的狀態(tài)對(duì)象。在此后的描述中,我們將這個(gè)鏈表稱為“狀態(tài)對(duì)象鏈表”。下面我們來(lái)看一看實(shí)現(xiàn)這個(gè)機(jī)制的關(guān)鍵數(shù)據(jù)結(jié)構(gòu)在Python中,對(duì)于這個(gè)狀態(tài)對(duì)象鏈表的訪問(wèn),不必在GIL的保護(hù)下進(jìn)行。

因?yàn)閷?duì)于這個(gè)狀態(tài)對(duì)象鏈表,Python線程會(huì)創(chuàng)建一個(gè)獨(dú)立的鎖,專職對(duì)狀態(tài)對(duì)象鏈表進(jìn)行保護(hù)。這個(gè)鎖的創(chuàng)建是在Python進(jìn)行初始化的時(shí)候完成的。PyThread_create_key將創(chuàng)建一個(gè)新的key。注意,這里的key都是一個(gè)整數(shù),而且,當(dāng)PyThread_create_key***次被調(diào)用時(shí)(在_PyGILState_Init中的調(diào)用正是***次調(diào)用)。

會(huì)通過(guò)PyThread_allcate_lock創(chuàng)建一個(gè)keymutex。根據(jù)我們前面的分析,這個(gè)keymutex實(shí)際上和GIL一樣,都是一個(gè)PNRMUTEX結(jié)構(gòu)體,而在這個(gè)結(jié)構(gòu)體中,維護(hù)著一個(gè)Win32下的Event內(nèi)核對(duì)象。這個(gè)keymutex的功能就是用來(lái)互斥對(duì)狀態(tài)對(duì)象鏈表的訪問(wèn)。

在_PyGILState_Init中,創(chuàng)建的新key被Python維護(hù)的全局變量autoTLSkey接收,其中的TLS是Thread Local Store的縮寫(xiě),這個(gè)autoTLSkey將用作Python保存所有線程的狀態(tài)對(duì)象的一個(gè)參數(shù)。的key值。也就是說(shuō),狀態(tài)對(duì)象列表中所有key結(jié)構(gòu)體中的key值都會(huì)是autoTLSkey。哎,那位看官說(shuō)了,你看PyThread_create_key返回的是nkeys的遞增后的值啊。

就是說(shuō)每create一次,得到的結(jié)果都是不同的,怎么能說(shuō)所有的key都是一樣的呢?事實(shí)上,在整個(gè)Python的源碼中,PyThread_create_key只在_PyGILState_Init中被調(diào)用了,而這個(gè)_PyGILState_Init只會(huì)在Python運(yùn)行時(shí)環(huán)境初始化時(shí)調(diào)用一次。

責(zé)任編輯:chenqingxiang 來(lái)源: 比特網(wǎng)
相關(guān)推薦

2015-05-19 16:21:05

2010-08-26 09:40:00

2010-08-25 13:13:04

2010-06-07 09:03:33

MySQL大小寫(xiě)

2019-03-13 22:40:15

機(jī)器學(xué)習(xí)假設(shè)算法

2023-09-08 12:19:01

線程方法interrupt

2022-02-28 07:01:22

線程中斷interrupt

2025-02-06 03:14:38

2024-10-21 18:12:14

2010-02-03 14:15:18

Python 開(kāi)發(fā)

2010-02-22 10:06:17

Python調(diào)用

2010-02-03 17:42:30

2010-02-22 14:13:38

安裝Python

2018-03-30 09:21:30

程序員網(wǎng)絡(luò)招聘

2010-02-02 18:20:43

Python編寫(xiě)

2010-02-03 15:40:37

Python函數(shù)

2010-02-24 15:27:26

Python數(shù)組

2021-03-15 12:23:24

Pythonyield代碼

2010-02-02 14:11:14

Python 進(jìn)行編程

2010-02-03 14:37:10

Python 開(kāi)發(fā)環(huán)境
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 一区二区三区欧美大片 | 亭亭五月激情 | 又爽又黄axxx片免费观看 | 高清久久 | 精品久久网 | 国产精品久久久一区二区三区 | 午夜伦理影院 | 亚洲成人久久久 | 亚洲国产精品一区二区久久 | 亚洲精品白浆高清久久久久久 | 亚洲精品一区二区冲田杏梨 | 日本精品一区二区三区视频 | 国产精品中文字幕在线播放 | 99久久国产精 | 日韩毛片在线视频 | 精品三级| 国产最新网址 | 天天天天操 | 亚洲嫩草| 欧美在线a | 欧美日韩手机在线观看 | av在线免费观看网站 | 国产精品久久久久久久 | 视频一区二区中文字幕 | 成人一区二区三区 | 伊人网国产 | 午夜电影网址 | 中文在线a在线 | 欧美日韩一 | 大学生a级毛片免费视频 | 狠狠操狠狠色 | 国产高清视频一区二区 | 五月天激情电影 | 日韩精品视频在线 | 成人小视频在线观看 | 午夜激情小视频 | 欧美精品一区二区三区蜜桃视频 | 日韩欧美在线观看 | 亚洲视频中文字幕 | 亚洲精品www. | 欧美在线视频一区二区 |