多執行緒非阻塞模式例項
阿新 • • 發佈:2019-02-20
多執行緒非阻塞模式到現在算是告一段落吧 雖然還有一些小的bug需要修正 總結一下 準備向後面進發
實現功能: 本程式主要實現遠端計算的功能 通過非阻塞套接字和多執行緒的結合 讓通訊變得高效 伺服器通過維護一個客戶端連結串列來實現對多個客戶響應 客戶端自身驗證表示式的正確性 當輸入Byebye時 伺服器回覆OK 此時客戶端斷開連線退出
主要模組和執行緒管理
伺服器:
主執行緒:初始化伺服器
執行伺服器
接收客戶端連線請求
建立客戶端節點
啟動對客戶端節點的服務
清理執行緒:
當伺服器執行時:
定期檢查清理已退出的客戶節點
當伺服器斷開時
斷開所有對客戶端的連線
清理客戶節點知道客戶端連結串列為空
客戶端類:接收執行緒:
接收客戶端的資料 並且處理
傳送執行緒:
傳送已經處理後的資料
客戶端:
主執行緒:
初始化客戶端
連線伺服器
執行處理輸入輸出
退出客戶端
接收資料執行緒:
接收伺服器資料 並且通知主執行緒顯示
傳送資料執行緒:
傳送主執行緒處理好的資料
關於執行緒同步:
從客戶端說起:
各個執行緒維持一個執行狀態變數bConning 用於執行緒迴圈外側檢測退
主執行緒在成功連線至伺服器後 bConning = TRUE 建立接收和傳送執行緒
這三個執行緒之間的互動:
一。正常互動
1.主執行緒在執行輸入處理完資料後放入SendBuf 通過hEventSendData來通知傳送執行緒(對SendBuf互斥訪問)
2.接收執行緒在將接收資料拷貝到RecvBuf後 通過hEventShowData來通知主執行緒顯示資料(對RecvBuf互斥訪問)
二。退出互動
1.接收執行緒先退出 設定bConning為FALSE 並SetEvent(hEventShowData)
對於傳送執行緒 檢測到bConning==FALSE 立即退出
對於主執行緒 在顯示資料前檢測bConning==FALSE 直接跳出 之後呼叫WaitForMutiObjects等 待發送執行緒和接收執行緒均退出
2.傳送執行緒先退出 設定bConning為FALSE 並SetEvent(hEventShowData) 如果不呼叫這條 主執行緒有可能無限等待顯示資料
對於接收執行緒 檢測到bConning為FALSE 退出迴圈
對於主執行緒 在顯示資料前檢測bConning==FALSE 跳出 等待接收和傳送均退出
3.主執行緒退出 當伺服器回覆"OK" 表示使用者輸入Byebye 此時斷開連線 等待接收和傳送退出
傳送和接收執行緒均檢測到bConning為FALSE 跳出迴圈
伺服器:
伺服器的客戶端類:
一。正常互動:
1.接收執行緒接收到資料後進行處理後 將資料拷貝到資料緩衝 用hEvent通知傳送執行緒傳送(對資料緩衝互斥訪問)
2.傳送執行緒傳送資料 並且恢復hEvent為無訊號
二。退出互動:這裡的傳送執行緒在退出迴圈後通過WaitForSingleObject(hThreadRecv)來設定整個客戶端的m_exit狀態
1.接收執行緒先退出 設定bConning為FALSE SetEvent(hEvent)
傳送執行緒在等待到hEvent時 先判斷bConning==FALSE時 退出迴圈
2.傳送執行緒先退出:設定bConning為FALSE 在跳出迴圈後 等待接收執行緒退出 設定m_exit=TRUE 用於清理執行緒清理節點 接收執行緒檢測到bConning=FALSE 跳出迴圈 退出
伺服器三個總維護執行緒:
一。正常互動: 類似前面 三個執行緒維護同一個狀態變數bServerRunning
1.伺服器主執行緒 初始化伺服器 並且根據使用者輸入開啟和斷開伺服器
在開啟伺服器時 設定bServerRunning = TRUE建立監聽和清理執行緒
在斷開伺服器時 設定bServerRunning=FALSE 並且通過清理執行緒結束的訊號量hServerEvent退出伺服器
2.伺服器監聽執行緒 不斷監聽來自客服端的連線請求 新增新的客戶端節點到客戶端連結串列 並交由清理執行緒維護
3.客戶端清理執行緒 測各個節點是否退出(m_Exit==TRUE) 並且移除連結串列 清理節點記憶體 當伺服器退出時 斷開所有客戶端連線 確保所有的客戶節點均已退出(cClientList.size() == 0)
二。退出互動: 1.伺服器主執行緒先退出:伺服器主執行緒不會先退出 在接收到斷開伺服器請求時 只是bServerRunning=FALSE
之後監聽執行緒停止 伺服器等待清理執行緒結束事hServerEvent後 退出
2.監聽執行緒先退出: 設定bServerRunning為FALSE 並退出
之後清理執行緒開始斷開所有客戶端連線 清理完成後SetEvent(hServerEvent)
而伺服器介面仍在等待輸入是否退出伺服器 一旦使用者輸入後(即使不是退出命令) 伺服器也將立即跳出迴圈 根據hServerEvent狀態退出(bug之一)
3.清理執行緒不會主動退出(除非遇到記憶體引用錯誤)
總的三個模組互動:
客戶端退出 那麼伺服器客戶端類的SendThread或RecvThread先後遇到錯誤退出 導致節點m_Exit==TRUE 被清理 伺服器退出 那麼客戶端的傳送和接收將失敗 也將導致退出
伺服器通過清理執行緒來同時斷開所有客戶連線
伺服器客戶端類通過m_Exit來告訴清理執行緒可以清理該節點
客戶端通過回覆內容為OK來得知使用者輸入了Byebye 並且開始退出自身
總結:
不管用何種方式通訊 相關聯的幾個執行緒總會用一個變數來控制所有的其他執行緒
對於非阻塞套接字 Recv Send Connect Accept等都需要套上一個基於共同控制變數或者永真的迴圈來實現對WSAEWOULDBLOCK的返回重試
對於通過事件訊號量來通知的兩個執行緒 例如生產者 消費者(生產者生產好了通過hEvent通知消費者) 當生產者退出時 一定要通過該訊號量來通知消費者 以免消費者阻塞於WaitForSingleObject 而消費者在等到訊號量時 也一定要檢測生產者是否已經退出(或者是說在這裡的斷開了連線) 以免傳送或接收未知的資料
對於有訊號量控制的兩個同步執行緒 要注意是否有共同訪問的資料 要時刻記得對資料進行互斥訪問
退出伺服器監聽執行緒: