欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python實(shí)現(xiàn)線程池工作模式的案例詳解

 更新時間:2022年06月29日 08:51:49   作者:TiAmo?zhang  
這篇文章給大家介紹Python實(shí)現(xiàn)線程池工作模式的相關(guān)知識,本文基于Socket通信方法,自定義數(shù)據(jù)交換協(xié)議,圍繞蘋果樹病蟲害識別需求,迭代構(gòu)建了客戶機(jī)/服務(wù)器模式的智能桌面App,感興趣的朋友跟隨小編一起看看吧

本文章基于蘋果樹病蟲害預(yù)測模型,自定義應(yīng)用層通信邏輯,設(shè)計服務(wù)器與客戶機(jī)??蛻魴C(jī)向服務(wù)器發(fā)送圖像數(shù)據(jù),服務(wù)器回送預(yù)測結(jié)果。為增強(qiáng)服務(wù)器的可靠性與可擴(kuò)展性,服務(wù)器端采用線程池工作模式。為了增強(qiáng)客戶機(jī)的可操作性,客戶機(jī)采用PyQt5完成圖形化界面設(shè)計。

01、客戶機(jī)/服務(wù)器通信邏輯

客戶機(jī)與服務(wù)器通信邏輯如圖1所示。

■圖1 智能桌面App的客戶機(jī)/服務(wù)器通信邏輯

02、數(shù)據(jù)交換協(xié)議

客戶機(jī)與服務(wù)器之間一次信息往返的協(xié)議會話過程,定義為圖2所示的邏輯時序。

■ 圖2 應(yīng)用層通信協(xié)議

協(xié)議會話邏輯解析:

(1)消息交換基于消息頭機(jī)制。消息頭中包含消息類型和消息長度。消息類型包含圖像消息與下線消息。

(2)用Json格式的數(shù)據(jù)表示消息頭。圖像數(shù)據(jù)用base64編碼與解碼。

(3)發(fā)送數(shù)據(jù)分兩個步驟完成,首先發(fā)送消息頭,然后發(fā)送消息內(nèi)容。

(4)接收數(shù)據(jù)分兩個步驟完成,首先接收消息頭,然后接收消息內(nèi)容。

消息頭的結(jié)構(gòu)設(shè)計如圖3所示,消息頭的固定長度為128字節(jié),包含消息類型(msg_type)與消息內(nèi)容長度(msg_len)兩個字段。

■圖3 消息頭的結(jié)構(gòu)

消息類型包括:

(1)CLIENT_IMAGE:表示收到來自客戶機(jī)的圖像數(shù)據(jù)。

(2)CLIENT_MESSAGE:表示收到來自客戶機(jī)的下線消息。

消息內(nèi)容長度用消息包含的字符數(shù)表示。對于圖像數(shù)據(jù)而言,因為采用base64編碼,其傳輸?shù)臄?shù)據(jù)也是字符消息。

消息頭的長度在服務(wù)器與客戶機(jī)兩端均約定為128字節(jié),用常量MSG_HEADER_LEN定義。發(fā)送消息頭之前,需要檢查消息的長度,如果不足128字節(jié),其左側(cè)用字節(jié)型空格字符填充。

03、服務(wù)器主體邏輯

根據(jù)圖1描述的服務(wù)器邏輯,完成服務(wù)器的主體邏輯設(shè)計,如程序段P7.1所示。

第32~39行定義服務(wù)器端的主循環(huán),處理客戶機(jī)連接,采用的是一客戶一線程模式。服務(wù)器會話線程定義為handle_client模塊,主線程向會話線程傳遞三個參數(shù):

(1)client_socket: 會話套接字

(2)client_addr: 客戶機(jī)地址

(3)model: 用于預(yù)測的智能模型

運(yùn)行服務(wù)器程序,觀察輸出結(jié)果,此時服務(wù)器雖然處于偵聽連接的狀態(tài),但是由于handle_client模塊還沒有實(shí)現(xiàn),故無法處理來自客戶機(jī)的各種消息。

04、服務(wù)器會話線程

服務(wù)器會話線程包括接收數(shù)據(jù)與發(fā)送數(shù)據(jù)兩個模塊,對應(yīng)圖1中的內(nèi)循環(huán)。服務(wù)器完成數(shù)據(jù)接收后,需要回送預(yù)測結(jié)果或者確認(rèn)消息給客戶機(jī),所以將接收數(shù)據(jù)與發(fā)送數(shù)據(jù)的邏輯定義在同一函數(shù)模塊handle_client中,收發(fā)數(shù)據(jù)的邏輯流程如圖4所示。

■圖4 服務(wù)器收發(fā)數(shù)據(jù)會話線程邏輯

會話線程的主邏輯是一個循環(huán),循環(huán)條件為遠(yuǎn)程客戶機(jī)是否結(jié)束會話,邏輯流程解析如下:

(1)如果客戶機(jī)斷開了與服務(wù)器的連接,會話線程結(jié)束。

(2)在連接正常的情況下,服務(wù)器首先接收來自客戶機(jī)的消息頭,解析消息頭,根據(jù)消息類型,分為一般消息與圖像消息。

(3)如果是圖像消息,則通過一個循環(huán),根據(jù)圖像的大小完成數(shù)據(jù)接收,然后經(jīng)過base64解碼、圖像變換(調(diào)整顏色模式、歸一化、縮放)、模型預(yù)測、重構(gòu)預(yù)測結(jié)果、定義消息頭、回送消息頭、回送預(yù)測結(jié)果。回到步驟(1)。

(4)如果是一般消息,則繼續(xù)判斷是否為下線消息。

(5)如果是下線消息,則更新連接數(shù)量,定義下線消息(原消息加上時間戳),定義消息頭,回送消息頭,回送消息內(nèi)容,會話線程結(jié)束。

(6)如果不是下線消息,則做其他消息處理,為簡化設(shè)計,其他消息處理模塊暫不編程,留作擴(kuò)展。回到步驟(1)。

會話線程handle_client的邏輯實(shí)現(xiàn)如程序段P7.2所示。

第46行–第51行定義的循環(huán)結(jié)構(gòu),根據(jù)圖像數(shù)據(jù)的長度msg_len完成數(shù)據(jù)接收工作。

運(yùn)行服務(wù)器程序,輸出結(jié)果為:

服務(wù)器開始在('192.168.0.102', 5050)偵聽...

待客戶機(jī)程序完成后,再做聯(lián)合測試。

05、客戶機(jī)主體邏輯

新建主程序MyClient.py。根據(jù)圖1描述的客戶機(jī)邏輯,完成客戶機(jī)的主體邏輯設(shè)計,其主要模塊如圖5所示。

模塊send_image_data發(fā)送圖像數(shù)據(jù),模塊send_down_msg發(fā)送下線消息,模塊recv_message是用于接收服務(wù)器消息的會話線程,類模塊GUI(QMainWindow)負(fù)責(zé)構(gòu)建客戶機(jī)圖形化界面。主程序完成主控邏輯設(shè)計。

■圖5 客戶機(jī)程序模塊結(jié)構(gòu)

客戶機(jī)的消息結(jié)構(gòu)定義如圖3所示,與服務(wù)器保持一致。消息的收發(fā)邏輯,如圖2所示,亦與服務(wù)器保持一致。

客戶機(jī)主體邏輯如程序段P7.3所示。

首先運(yùn)行服務(wù)器程序,然后運(yùn)行測試客戶機(jī)程序。目前客戶機(jī)還做不了具體工作,輸入字符Q退出客戶機(jī)主循環(huán)。

06、客戶機(jī)發(fā)送數(shù)據(jù)

客戶機(jī)向服務(wù)器發(fā)送的數(shù)據(jù)有兩種類型,一是圖像數(shù)據(jù),一是下線消息。發(fā)送圖像數(shù)據(jù)的流程如圖6所示。

■圖6 發(fā)送圖像數(shù)據(jù)流程

程序段P7.4描述了發(fā)送圖像數(shù)據(jù)模塊send_image_data的完整邏輯。

07、客戶機(jī)接收數(shù)據(jù)

客戶機(jī)定義了線程函數(shù)recv_message,用于接收兩類數(shù)據(jù),一是普通消息(下線消息等),二是預(yù)測消息(預(yù)測結(jié)果)。消息處理流程如圖7所示,分步描述如下。

(1)進(jìn)入消息循環(huán),接收消息頭。

(2)如果消息頭為空,轉(zhuǎn)到步驟(1)。

(3)如果消息頭非空,則解析消息頭,獲取消息類型與消息長度。

(4)如果是普通消息,則接收消息內(nèi)容,進(jìn)一步判斷是否為下線消息。

(5)如果是下線消息,則跳出消息循環(huán),轉(zhuǎn)到步驟(9)。

(6)如果非下線消息,則轉(zhuǎn)到步驟(1)。

(7)如果不是普通消息,則判斷是否為預(yù)測消息,如果不是預(yù)測消息,則轉(zhuǎn)到步驟(1)。

(8)如果是預(yù)測消息,則接收消息內(nèi)容,解析消息內(nèi)容,將預(yù)測結(jié)果存入隊列中,顯示預(yù)測結(jié)果。轉(zhuǎn)到步驟(1)。

(9)顯示下線消息,消息接收線程結(jié)束。

■圖7 客戶機(jī)接收消息邏輯流程

程序段P7.6描述了接收消息線程函數(shù)recv_message的完整邏輯。

將\dataset\images目錄下的圖像文件Test_0.jpg、Test_7.jpg拷貝到根目錄下。

運(yùn)行服務(wù)器程序,然后運(yùn)行客戶機(jī)程序,做聯(lián)合測試。

客戶機(jī)輸入待遇測的圖像文件名稱Test_0.jpg,回車后發(fā)送圖像數(shù)據(jù),服務(wù)器返回預(yù)測結(jié)果??蛻魴C(jī)輸入字符Q,結(jié)束客戶機(jī)。完成此次客戶機(jī)與服務(wù)器的通信后,服務(wù)器與客戶機(jī)的狀態(tài)信息如圖8所示。

■圖8 客戶機(jī)與服務(wù)器聯(lián)合測試

此時服務(wù)器工作于一客戶一線程模式,啟動多個客戶端,可做聯(lián)合測試。

08、客戶機(jī)界面設(shè)計

為了增強(qiáng)客戶機(jī)的可操作性,基于PyQt5框架為客戶機(jī)設(shè)計圖形化界面,界面布局及其控件名稱如圖9所示。

■圖9 客戶機(jī)圖形化界面布局

定義圖形化界面類GUI(QMainWindow)封裝圖9所示的控件及其事件函數(shù)。

運(yùn)行服務(wù)器,然后運(yùn)行客戶機(jī),從chapter7的根目錄中加載圖像Test_0.jpg,觀察圖像特點(diǎn)。然后單擊“預(yù)測”按鈕,觀察服務(wù)器反饋的預(yù)測結(jié)果,如圖10所示。

■圖10 客戶機(jī)圖形化界面測試結(jié)果

09、線程池

服務(wù)器現(xiàn)有的工作模式為一客戶一線程,即為每一個連接到服務(wù)器的客戶機(jī)創(chuàng)建獨(dú)立的會話線程,當(dāng)客戶機(jī)并發(fā)量較大時,服務(wù)器往往面臨資源枯竭的挑戰(zhàn)。

線程池模式可以有效平衡服務(wù)器負(fù)載能力,與一客戶一線程模式相比,其主要優(yōu)點(diǎn)有:

(1)通過重用已存在的線程,降低線程創(chuàng)建和銷毀造成的額外消耗。

(2)提高系統(tǒng)響應(yīng)速度,當(dāng)有新任務(wù)到達(dá)時,通過復(fù)用已存在的線程便能立即執(zhí)行,無需等待新線程的創(chuàng)建。

(3)控制資源消耗,將并發(fā)線程數(shù)量限制在合理的區(qū)間。

(4)針對工作線程提供了更多的控制能力,例如線程延時、定時等。

Python的線程池定義在concurrent.futures包中,使用ThreadPoolExecutor類創(chuàng)建線程池。線程池調(diào)度任務(wù)過程如圖11所示。

■圖11 線程池調(diào)度任務(wù)示意圖

將一客戶一線程模式修改為線程池模式,只需做以下改動:

(1)導(dǎo)入線程池類ThreadPoolExecutor。在服務(wù)器端添加語句:

from concurrent.futures import ThreadPoolExecutor # 線程池類

(2)在服務(wù)器主線程的while循環(huán)前面添加創(chuàng)建線程池的語句:

pool = ThreadPoolExecutor(max_workers=5) # 創(chuàng)建線程池,指定工作線程數(shù)量為5

此處如果省略參數(shù)max_workers,則線程池默認(rèn)工作線程數(shù)量是CPU數(shù)量的5倍。考慮到線程池往往應(yīng)用于需要大量I/O交換的場景,而不是CPU計算密集型的場景,故工作線程的數(shù)量應(yīng)該超過CPU的數(shù)量。

(3)用線程池調(diào)度語句替換原有的線程創(chuàng)建語句。

# 建立與客戶機(jī)會話的線程,一客戶一線程
client_thread = threading.Thread(target=handle_client, args=(new_socket, new_addr, model))
client_thread.start()

替換為:

pool.submit(handle_client,new_socket, new_addr, model) # 創(chuàng)建線程任務(wù),提交到線程池

(4)在主程序末尾,while循環(huán)外部,添加關(guān)閉線程池的語句,釋放資源:

pool.shutdown(wait=True) # 關(guān)閉線程池

執(zhí)行shutdown后,線程池將不再接受新任務(wù)。參數(shù)wait默認(rèn)為True,表示關(guān)閉線程池之前需要等待所有工作線程結(jié)束。

10、聯(lián)合測試

為便于觀察,將服務(wù)器線程池的工作線程數(shù)量調(diào)整為2。啟動服務(wù)器,然后啟動四個客戶機(jī),標(biāo)識為客戶機(jī)1、客戶機(jī)2、客戶機(jī)3、客戶機(jī)4。

四個客戶機(jī)從dataset\images目錄中選擇四幅不同的測試圖片,

假定客戶機(jī)1選擇的圖片是Test_17.jpg,客戶機(jī)2選擇的是Test_152.jpg,客戶機(jī)3選擇的是Test_190.jpg,客戶機(jī)4選擇的是Test_1572.jpg,然后依次點(diǎn)擊客戶機(jī)1、客戶機(jī)2、客戶機(jī)3、客戶機(jī)4的“預(yù)測”按鈕,觀察預(yù)測結(jié)果。

可以看到,只有客戶機(jī)1、客戶機(jī)2立即反饋了預(yù)測結(jié)果,而客戶機(jī)3、客戶機(jī)4雖然已經(jīng)連接到服務(wù)器,卻并沒有立即得到預(yù)測結(jié)果,原因是服務(wù)器線程池大小為2,客戶機(jī)3、客戶機(jī)4需要在任務(wù)隊列等待。

客戶機(jī)1顯示結(jié)果如圖12所示。

■圖12 客戶機(jī)1的預(yù)測結(jié)果

客戶機(jī)2顯示結(jié)果圖13所示。

■圖13 客戶機(jī)2的預(yù)測結(jié)果

客戶機(jī)3顯示結(jié)果如圖14所示。由于服務(wù)器線程池大小為2,所以客戶機(jī)1與客戶機(jī)2占用工作線程后,客戶機(jī)3只能進(jìn)入任務(wù)隊列等待。

■圖14 客戶機(jī)3處于等待中

客戶機(jī)4顯示結(jié)果如圖15所示。同樣,客戶機(jī)4也只能進(jìn)入服務(wù)器的任務(wù)隊列等待。

■圖15 客戶機(jī)4處于等待中

關(guān)閉客戶機(jī)1,則會自動釋放客戶機(jī)1占用的工作線程,此時排隊中的客戶機(jī)3會立即得到相應(yīng),其結(jié)果如圖16所示。

■圖16 客戶機(jī)3得到服務(wù)器響應(yīng)

此時只有客戶機(jī)4仍處于等待中。如果繼續(xù)關(guān)閉客戶機(jī)2,則客戶機(jī)4會得到立即響應(yīng),其預(yù)測結(jié)果如圖17所示。

■圖17 客戶機(jī)4得到服務(wù)器響應(yīng)

關(guān)閉客戶機(jī)3、關(guān)閉客戶機(jī)4。整個會話期間,服務(wù)器狀態(tài)監(jiān)控界面的信息提示如下:

仔細(xì)閱讀服務(wù)器的狀態(tài)提示信息,與客戶機(jī)的操作相對照,可以更精準(zhǔn)地把握客戶機(jī)與服務(wù)器的全程會話邏輯。

11、小結(jié)

本文基于Socket通信方法,自定義數(shù)據(jù)交換協(xié)議,圍繞蘋果樹病蟲害識別需求,迭代構(gòu)建了客戶機(jī)/服務(wù)器模式的智能桌面App。圖像數(shù)據(jù)的發(fā)送采用base64編碼方式,消息頭、消息內(nèi)容采用Json數(shù)據(jù)格式。服務(wù)器端采用一客戶一線程和線程池技術(shù)支持并發(fā)訪問,客戶機(jī)采用基于PyQt5的圖像化界面技術(shù)提高其可操作性。基于Socket技術(shù)的網(wǎng)絡(luò)編程,在客戶機(jī)與服務(wù)器兩端提供了更多的設(shè)計靈活性。

到此這篇關(guān)于Python實(shí)現(xiàn)線程池工作模式的文章就介紹到這了,更多相關(guān)Python線程池工作模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論