91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何正確區分Python線程

發布時間:2021-11-02 17:36:27 來源:億速云 閱讀:296 作者:柒染 欄目:編程語言

這篇文章給大家介紹如何正確區分Python線程,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

在Python語言中Python線程可以從這里開始與主線程對GIL的競爭,在t_bootstrap中,申請完了GIL,也就是說子線程也就獲得了GIL,使其始終保存著活動線程的狀態對象。

當PyEval_AcquireThread結束之后,子線程也就獲得了GIL,并且做好了一切執行的準備。接下來子線程通過PyEval_ CallObjectWithKeywords,將最終調用我們已經非常熟悉的PyEval_EvalFrameEx。

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

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

實際上在PyEval_EvalFrameEx中,圖15-2中顯示的Python內部維護的那個模擬時鐘中斷會不斷地激活線程的調度機制,在子線程和主線程之間不斷地進行切換。從而真正實現多線程機制,當然,這一點我們將在后面詳細剖析。現在我們感興趣的是子線程在PyEval_AcquireThreade中到底做了什么。

到這里,了解了PyEval_AcquireThread,似乎創建線程的機制都清晰了。但實際上,有一個非常重要的機制——線程狀態保護機制——隱藏在了一個毫不起眼的地方:PyThreadState_New。

[threadmodule.c]   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)   {       unsigned long rv;       callobj obj;       obj.id = -1;    /* guilty until proved innocent */       obj.func = func;       obj.arg = arg;       obj.done = CreateSemaphore(NULL, 0, 1, NULL);       rv = _beginthread(bootstrap, _pythread_stacksize, &obj); /* use default stack size */       if (rv == (unsigned long)-1) {           //創建raw thread失敗           obj.id = -1;       }       else {           WaitForSingleObject(obj.done, INFINITE);       }       CloseHandle((HANDLE)obj.done);       return obj.id;   }

這個機制對于理解Python線程的創建和維護是非常關鍵的。要剖析線程狀態的保護機制,我們首先需要回顧一下線程狀態。在Python中,每一個Python線程都會有一個線程狀態對象與之關聯。

在線程狀態對象中,記錄了每一個線程所獨有的一些信息。實際上,在剖析Python的初始化過程時,我們曾經見過這個對象。每一個線程對應的線程狀態對象都保存著這個線程當前的PyFrameObject對象,線程的id這樣一些信息。有時候,線程是需要訪問這些信息的。

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

我們知道,在操作系統從進程A切換到進程B時,首先會保存進程A的上下文環境,再進行切換;當從進程B切換回進程A時,又會恢復進程A的上下文環境,這樣就保證了進程A始終是在屬于自己的上下文環境中運行。

這里的線程狀態對象就等同于進程的上下文,Python同樣會有一套存儲、恢復線程狀態對象的機制。同時,在Python內部,維護著一個全局變量:PyThreadState * _PyThread- State_Current。

當前活動線程所對應的線程狀態對象就保存在這個變量里,當Python調度線程時,會將被激活的線程所對應的線程狀態對象賦給_PyThreadState_Current,使其始終保存著活動線程的狀態對象。

這就引出了這樣的一個問題:Python如何在調度進程時,獲得被激活線程對應的狀態對象?Python內部會通過一個單向鏈表來管理所有的Python線程的狀態對象,當需要尋找一個線程對應的狀態對象時。

  • 初次接觸Python部署問題解析

  • 強大快捷的Python操作語言全解析

  • 對Python 調試器豐富資源介紹

  • 對Python交互式技巧總結之談

  • 如何正確認識Python 源文件

就遍歷這個鏈表,搜索其對應的狀態對象。在此后的描述中,我們將這個鏈表稱為“狀態對象鏈表”。下面我們來看一看實現這個機制的關鍵數據結構在Python中,對于這個狀態對象鏈表的訪問,不必在GIL的保護下進行。

因為對于這個狀態對象鏈表,Python線程會創建一個獨立的鎖,專職對狀態對象鏈表進行保護。這個鎖的創建是在Python進行初始化的時候完成的。PyThread_create_key將創建一個新的key。注意,這里的key都是一個整數,而且,當PyThread_create_key***次被調用時(在_PyGILState_Init中的調用正是***次調用)。

會通過PyThread_allcate_lock創建一個keymutex。根據我們前面的分析,這個keymutex實際上和GIL一樣,都是一個PNRMUTEX結構體,而在這個結構體中,維護著一個Win32下的Event內核對象。這個keymutex的功能就是用來互斥對狀態對象鏈表的訪問。

在_PyGILState_Init中,創建的新key被Python維護的全局變量autoTLSkey接收,其中的TLS是Thread Local Store的縮寫,這個autoTLSkey將用作Python保存所有線程的狀態對象的一個參數。的key值。也就是說,狀態對象列表中所有key結構體中的key值都會是autoTLSkey。哎,那位看官說了,你看PyThread_create_key返回的是nkeys的遞增后的值啊。

就是說每create一次,得到的結果都是不同的,怎么能說所有的key都是一樣的呢?事實上,在整個Python的源碼中,PyThread_create_key只在_PyGILState_Init中被調用了,而這個_PyGILState_Init只會在Python運行時環境初始化時調用一次。

關于如何正確區分Python線程就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

孟州市| 凉城县| 望都县| 平阴县| 仙游县| 太仆寺旗| 米泉市| 阳朔县| 繁昌县| 鄂温| 南乐县| 邢台市| 滕州市| 峨眉山市| 青浦区| 泗水县| 凭祥市| 澄江县| 德令哈市| 鄂托克前旗| 桂平市| 高雄县| 平凉市| 英吉沙县| 延庆县| 平塘县| 洪湖市| 兴化市| 岢岚县| 金堂县| 瑞金市| 平顺县| 衡山县| 锡林郭勒盟| 辽源市| 双流县| 宝山区| 恩平市| 昔阳县| 苍南县| 临颍县|