• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

               C++ 技術(shù)中心

               :: 首頁 :: 聯(lián)系 ::  :: 管理
              160 Posts :: 0 Stories :: 87 Comments :: 0 Trackbacks

            公告

            鄭重聲明:本BLOG所發(fā)表的原創(chuàng)文章,作者保留一切權(quán)利。必須經(jīng)過作者本人同意后方可轉(zhuǎn)載,并注名作者(天空)和出處(CppBlog.com)。作者Email:coder@luckcoder.com

            留言簿(27)

            搜索

            •  

            最新隨筆

            最新評(píng)論

            評(píng)論排行榜

            腳本語言是快速編寫富有彈性的代碼的重要方法之一,在 Unix 系統(tǒng)自動(dòng)化管理中已經(jīng)應(yīng)用了多種腳本語言。現(xiàn)在,在許多應(yīng)用開發(fā)中,也提供了腳本層,這大大方便用戶實(shí)現(xiàn)通用任務(wù)自動(dòng)處理或者編寫應(yīng)用擴(kuò)展,許多成功的應(yīng)用,諸如 GIMPEmacsMS OfficePhotoShopAutoCAD 等都應(yīng)用了腳本技術(shù)。在某種意義上,一切皆可腳本化。

            在另一篇文章中,我們已經(jīng)介紹了如何在 C 應(yīng)用中嵌入 Python 語言,通過這項(xiàng)技術(shù),可以讓應(yīng)用的高級(jí)用戶來修改或定制化他們的程序,你可以充分利用 Python 的語言能力而不用自己去實(shí)現(xiàn)嵌入語言。Python 是一個(gè)不錯(cuò)的的選擇,因?yàn)樗峁┝烁蓛糁庇^的 C 語言 API。關(guān)于如何在 C 應(yīng)用中嵌入 Python 解釋器,你可以參考:Python成為嵌入式語言一文。

            現(xiàn)在我們來更深入地探討一些問題。 鑒于許多復(fù)雜的應(yīng)用都會(huì)利用多線程技術(shù),本文將著重介紹如何創(chuàng)建線程安全的界面來調(diào)用Python解釋器。

            這里的所有例子都是用 Python 2.7.2,所有的 Python 函數(shù)都以extern “C”定義,因此對(duì)于 C C++,其使用是別無二致的。
            Python C 和線程

            C程序中創(chuàng)建執(zhí)行線程是很簡單的。在 Linux 中,通常的做法是使用 POSIX 線程(pthread) API 并調(diào)用 pthread_create 函數(shù)。關(guān)于如何使用 pthreads,你可以參考 Felix Garcia Javier Fernandez 著的 “POSIX Thread Libraries”一文。為了支持多線程, Python 使用了互斥使訪問內(nèi)部數(shù)據(jù)結(jié)構(gòu)串行化。這種互斥即全局解釋器鎖 – global interpreter lock”,當(dāng)某個(gè)線程想使用 Python C API的時(shí)候,它必須獲得 全局解釋器鎖,這避免了會(huì)導(dǎo)致解析器狀態(tài)崩潰的競(jìng)爭(zhēng)條件(race condition)

            互斥的鎖定和釋放是通過 PyEval_AcquireLock Eval_ReleaseLock 來描述的。調(diào)用了 PyEval_AcquireLock 之后,可以安全地假定你的線程已經(jīng)持有了鎖,其他相關(guān)線程不是被阻塞就是在執(zhí)行與 Python 解析器無關(guān)的代碼。現(xiàn)在你可以任意調(diào)用 Python 函數(shù)了。一旦取得了鎖,你必須確保調(diào)用 PyEval_ReleaseLock 來釋放它,否則就會(huì)導(dǎo)致線程死鎖并凍結(jié)其他 Python 線程。

            更復(fù)雜的情況是,每個(gè)運(yùn)行 Python 的線程維護(hù)著自己的狀態(tài)信息。這些和特定線程相關(guān)的數(shù)據(jù)存儲(chǔ)在稱為 PyThreadState 的對(duì)象中。當(dāng)在多線程應(yīng)用中用 C 語言調(diào)用 Python API 函數(shù)時(shí),你必須維護(hù)自己的 PyThreadState 對(duì)象以便能安全地執(zhí)行并發(fā)的 Python 代碼。

            如果你對(duì)開發(fā)多線程應(yīng)用相當(dāng)有經(jīng)驗(yàn),你可能會(huì)發(fā)現(xiàn)全局解釋器鎖的概念相當(dāng)不方便。不過,現(xiàn)在它已經(jīng)不像首次出現(xiàn)時(shí)那樣糟糕了。當(dāng) Python 對(duì)腳本進(jìn)行解釋時(shí),它會(huì)定期切換出當(dāng)前 PyThreadState 對(duì)象并釋放全局解釋器鎖,從而將控制權(quán)釋放給其他線程。之前被阻塞的線程可以試圖鎖定全局解釋器鎖從而被運(yùn)行。有些時(shí)候,原來的線程會(huì)再次獲得全局解釋器鎖再次切回解釋器。

            這意味著當(dāng)調(diào)用 PyEval_SimpleString 時(shí),即使你持有全局解釋器鎖,其他線程仍有機(jī)會(huì)被執(zhí)行,這樣的副作用無可避免。另外,當(dāng)你調(diào)用以 C 語言寫就的 Python 模塊(包括許多內(nèi)置模塊) 存在著將控制權(quán)釋放給其他線程的可能性。基于這個(gè)原因,當(dāng)你用兩個(gè) C 線程來執(zhí)行計(jì)算密集的 Python 腳本,它們確實(shí)能分享 CPU 時(shí)間并發(fā)運(yùn)行,但由于全局解釋器鎖的存在,在多處理器的計(jì)算機(jī)上,Python 無法通過線程充分計(jì)算機(jī)的 CPU 處理能力。

            啟用線程支持
            在多線程的 C 程序使用 Python API 之前,必須調(diào)用一些初始化例程。如果編譯解釋器庫時(shí)啟用了多線程支持(通常情況如此),你就有了一個(gè)是否啟用線程的運(yùn)行時(shí)選項(xiàng)。除非你計(jì)劃使用線程,否則不建議啟用該選項(xiàng)。未啟用該選項(xiàng),Python 可以避免因互斥鎖定其內(nèi)部數(shù)據(jù)結(jié)構(gòu)而產(chǎn)生的系統(tǒng)開銷。但是如果你打算用 Python 來擴(kuò)展多線程應(yīng)用,你就需要在初始化解釋器的時(shí)候啟用線程支持。我個(gè)人建議,應(yīng)該在主線程執(zhí)行時(shí)就初始化 Python,最好是在應(yīng)用程序啟動(dòng)的時(shí)候,就調(diào)用下面兩行代碼:

            // initialize Python
            Py_Initialize();
            // initialize thread support
            PyEval_InitThreads();

            這兩個(gè)函數(shù)都返回 void,所以無需檢查錯(cuò)誤代碼。現(xiàn)在,我們可以假定 Python 解釋器已準(zhǔn)備好執(zhí)行 Python 代碼。Py_Initialize 分配解釋器庫使用的全局資源。調(diào)用PyEval_InitThreads 則啟用運(yùn)行時(shí)線程支持。這導(dǎo)致 Python 啟用其內(nèi)部的互斥鎖機(jī)制,用于解釋器內(nèi)代碼關(guān)鍵部分的系列化訪問。此函數(shù)的另一個(gè)作用是鎖定全局解釋器鎖。該函數(shù)完成后,需要由用戶負(fù)責(zé)釋放該鎖。不過,在釋放鎖之前, 你應(yīng)該捕獲當(dāng)前 PyThreadState 對(duì)象的指針。后續(xù)創(chuàng)建新的 Python 線程以及結(jié)束使用 Python 時(shí)要正確關(guān)閉解釋器,都需要用到該對(duì)象。下面這段代碼用來捕獲 PyThreadState 對(duì)象指針:

            PyThreadState * mainThreadState = NULL;
            // save a pointer to the main PyThreadState object
            mainThreadState = PyThreadState_Get();
            // release the lock
            PyEval_ReleaseLock();


            創(chuàng)建新的執(zhí)行線程

            Python 里,每個(gè)執(zhí)行 Python 代碼的線程都需要一個(gè) PyThreadState 對(duì)象。解釋器使用此對(duì)象來管理每個(gè)線程獨(dú)立的數(shù)據(jù)空間。理論上,這意味著一個(gè)線程中的動(dòng)作不會(huì)牽涉到另一個(gè)線程的狀態(tài)。例如,你在一個(gè)線程中拋出異常,其他 Python 代碼片段仍會(huì)繼續(xù)運(yùn)行,就好象什么事情都沒有發(fā)生一樣。你必須幫助 Python 管理每個(gè)線程的數(shù)據(jù)。為此,你需要為每個(gè)執(zhí)行 Python 代碼的 C 線程手工創(chuàng)建一個(gè) PyThreadState 對(duì)象.要?jiǎng)?chuàng)建 PyThreadState 對(duì)象,你需要用到既有的 PyInterpreterState 對(duì)象。PyInterpreterState 對(duì)象帶有為所有參與的線程所共享的信息。當(dāng)你初始化 Python 時(shí),它就會(huì)創(chuàng)建一個(gè) PyInterpreterState 對(duì)象,并將它附加在主線程的 PyThreadState 對(duì)象上。你可以使用該解釋器對(duì)象為你自己的 C 現(xiàn)成創(chuàng)建新的 PyThreadState。請(qǐng)參考下面代碼

            // get the global lock
            PyEval_AcquireLock();
            // get a reference to the PyInterpreterState
            PyInterpreterState * mainInterpreterState = mainThreadState->interp;
            // create a thread state object for this thread
            PyThreadState * myThreadState = PyThreadState_New(mainInterpreterState);
            // free the lock
            PyEval_ReleaseLock();
            執(zhí)行 Python 代碼
            現(xiàn)在我們已創(chuàng)建 PyThreadState 對(duì)象,你的 C 線程就可以開始使用 Python API 執(zhí)行 Python 腳本。從 C 線程執(zhí)行 Python 代碼時(shí),你必須遵守一些簡單的規(guī)則。首先,您在進(jìn)行任何會(huì)改變當(dāng)前線程狀態(tài)的操作前必須持有全局解釋器鎖。第二,必須在執(zhí)行任何 Python 代碼之前,必須將該線程特定的 PyThreadState 對(duì)象加載到解釋器。一旦您已經(jīng)滿足這些條件,您可以通過諸如 PyEval_SimpleString 函數(shù)來執(zhí)行任意的 Python 代碼,并記得在執(zhí)行結(jié)束時(shí)切出 PyThreadState 對(duì)象并釋放全局解釋器鎖。請(qǐng)參考下面代碼,注意代碼中鎖定、 切換、 執(zhí)行、 切換,解鎖的對(duì)稱關(guān)系:
            // grab the global interpreter lock
            PyEval_AcquireLock();
            // swap in my thread state
            PyThreadState_Swap(myThreadState);
            // execute some python code
            PyEval_SimpleString("import sys\n");
            PyEval_SimpleString(
            "sys.stdout.write(‘Hello from a C thread!\n‘)\n");
            // clear the thread state
            PyThreadState_Swap(NULL);
            // release our hold on the global interpreter
            PyEval_ReleaseLock();


            清除線程
            一旦你的 C 線程不再需要 Python 解釋器,你必須釋放相關(guān)資源。為此,需要?jiǎng)h除該線程的 PyThreadState 對(duì)象,相關(guān)代碼如下:

            // grab the lock
            PyEval_AcquireLock();
            // swap my thread state out of the interpreter
            PyThreadState_Swap(NULL);
            // clear out any cruft from thread state object
            PyThreadState_Clear(myThreadState);
            // delete my thread state object
            PyThreadState_Delete(myThreadState);
            // release the lock
            PyEval_ReleaseLock();

            通過使用 Python API ,這個(gè)線程很有效率地完成了上述工作。現(xiàn)在你可以安全地調(diào)用 pthread_ext 來結(jié)束該線程的運(yùn)行。
            關(guān)閉解釋器
            一旦應(yīng)用不在需要 Python 解釋器,你可以用下面的代碼將 Python 關(guān)閉掉:

            // shut down the interpreter
            PyEval_AcquireLock();
            Py_Finalize();

            注意:因?yàn)?/font> Python 已經(jīng)被關(guān)系,這里就不需要釋放鎖。請(qǐng)確保在調(diào)用 Py_Finalize 之前用 PyThreadState_Clear PyThreadState_Delete 刪除掉所有線程狀態(tài)對(duì)象。

            小結(jié):
            作為嵌入式語言,Python 是一個(gè)不錯(cuò)的選擇。Python 解釋器同時(shí)支持嵌入和擴(kuò)展,它允許 C 應(yīng)用程序代碼和嵌入的 Python 腳本之間的雙向通信。此外,多線程支持促進(jìn)了與多線程應(yīng)用程序的集成,而且不影響性能。

            你可以從本文的后面下載有關(guān)案例Python embedded HTTP Server (29),該案例實(shí)現(xiàn)了一個(gè)內(nèi)嵌 Python 解釋器的多線程 HTTP 服務(wù)器。此外我推薦您去 http://www.python.org/docs/api/ 閱讀有關(guān)的 Python C API 文檔。另外 Python 解釋器本身的代碼也是一個(gè)很有價(jià)值的參考。

             

            posted on 2013-12-06 10:34 C++技術(shù)中心 閱讀(9544) 評(píng)論(2)  編輯 收藏 引用 所屬分類: 其他編程

            Feedback

            # re: c++多線程調(diào)用python 2013-12-06 17:26 佛山華康醫(yī)院
            http://blog.sina.com.cn/s/blog_473fa3520100q50o.html  回復(fù)  更多評(píng)論
              

            # re: c++多線程調(diào)用python 2014-06-29 09:17 111111
            每個(gè)線程在執(zhí)行時(shí)都得鎖定GIL,還不影響性能?  回復(fù)  更多評(píng)論
              

            久久亚洲精品成人无码网站| 久久se精品一区精品二区国产| 久久精品无码一区二区三区日韩| 999久久久国产精品| 欧美精品福利视频一区二区三区久久久精品 | 伊人久久大香线蕉综合影院首页| 中文字幕热久久久久久久| 2021国内久久精品| 色天使久久综合网天天| 伊人色综合久久天天网| 久久久久亚洲精品日久生情 | 996久久国产精品线观看| 久久WWW免费人成—看片| 少妇精品久久久一区二区三区 | 99国产欧美久久久精品蜜芽| 久久久精品波多野结衣| 久久中文骚妇内射| 亚洲国产综合久久天堂| 亚洲综合婷婷久久| 亚洲午夜久久久久妓女影院| 国产午夜精品理论片久久| 2022年国产精品久久久久| 日本WV一本一道久久香蕉| 久久久久亚洲av成人无码电影| www久久久天天com| 亚洲精品国产字幕久久不卡| 久久精品无码一区二区app| 亚洲国产精品久久66| 久久精品a亚洲国产v高清不卡| 国产精品久久新婚兰兰| 日本高清无卡码一区二区久久 | 久久精品国产亚洲网站| 狠狠色婷婷久久一区二区| 亚洲а∨天堂久久精品9966| 久久久久无码精品国产app| 久久高潮一级毛片免费| 国产精品99久久久久久董美香| 俺来也俺去啦久久综合网| 狠色狠色狠狠色综合久久| 2021久久国自产拍精品| 99久久国产免费福利|