Muduo網絡庫實現 [九] - EventLoopThread模塊

目錄

設計思路

類的設計

模塊的實現

私有接口

公有接口?


設計思路

我們說過一個EventLoop要綁定一個線程未來該EventLoop所管理的所有的連接的操作都需要在這個EventLoop綁定的線程中進行,所以我們該如何實現將EventLoop和線程綁定呢?

按照我們前面實現的EventLoop的邏輯,構造函數的時候,_thread_id(std::this_thread::get_id()) 也就是EventLoop構造時就直接綁定了當前線程了,那么我們就需要先創建線程,然后在線程的入口函數中來創建一個EventLoop對象,這樣我們的EventLoop對象就和一個線程綁定了,創建完之后它可以將這個EventLoop的指針返回給TcpServer用來分配給新連接進行關聯。

那么我們可不可以先創建一批EventLoop對象再來綁定線程呢?

從技術的角度來說當然可以,我們只需要設置一個接口用來設置EventLoop的_thread_id就行了,但是這樣做會存在一個問題:當我們創建一批EventLoop的時候,再創建一批線程來進行綁定之前,是有一個時間窗口?的,在這個時間窗口中,如果有新連接到來,且有新事件到來,那么就會出現問題。

就好比一家繁忙的餐廳,你是經理需要分配服務員(線程)負責特定的餐桌區域(EventLoop)。正確的做法是:先雇傭服務員,培訓他們,然后才開門營業接待客人。但如果你決定先開門營業,讓餐桌區域準備好接待客人,然后才開始招聘和分配服務員,就會出現危險的時間窗口:

客人已經入座點餐(連接已建立),甚至食物已經準備好(事件已到來),但沒有服務員知道這些餐桌是他們負責的!結果就是客人坐在那里等待,沒人來服務他們,食物在廚房里變冷,而餐廳陷入混亂。

也就是我們的_thread_id還沒有切換到我們想要的線程id上,還在創建EventLoop的線程中,那么這時候操作就是由這個創建EventLoop的線程執行了,而后續切換線程之后,又會由新的線程來執行操作,那么就會出現一個連接的所有操作并不在一個線程中全部完成,可能會出現在多個線程中執行的情況,

再想象這樣一個場景:餐廳開始營業,最初由經理(創建EventLoop的線程)臨時擔任服務員的角色,開始接待客人和處理訂單。然后在客人就餐過程中,經理突然告訴新來的服務員:"從現在開始,這些桌子由你負責了",然后經理離開去做其他工作。

這會導致嚴重的混亂:

  • 服務員不知道這些客人已經點了什么菜
  • 不清楚客人的特殊要求或過敏信息
  • 不了解客人的用餐進度
  • 甚至可能重復上菜或忘記上某些菜品

客人的體驗會非常糟糕,因為他們的服務被分割在兩個不同的服務人員之間,沒有連貫性和一致性。

這是我們不想看到的,我們要確保一個連接的所有操作都在一個線程中執行,所以我們不能采取這種方案。

那么我們的方案:就只能是先創建線程再創建EventLop對象,后續會通過特定的方式返回這個EventLoop的指針交給TcpServer進行分配。

為了方便操作,我們可以設置一個新的模塊,也就是EventLoopThread模塊,專門用于創建一個線程并創建綁定一個EventLoop。

它內部有兩個成員,一個就是我們的線程對象,另一個就是我們的EventLoop的指針。

所以我們要設置線程入口函數,然后在線程入口函數中創建一個EventLoop對象,創建完之后執行EventLoop的Start來完成事件的循環。

但是這時候就會有一個問題,因為線程的創建到創建好一個EventLoop對象并設置指針變量是有一個時間窗口的,同時,在設置我們的指針成員的同時,有可能由其他的線程需要獲取這個指針,那么就會出現讀寫并發的情況,或者說這個指針變量會有線程安全問題。

我們還拿餐廳的例子進行加以理解

想象餐廳正在準備開業經理在招聘并培訓新的服務員。每個服務員需要熟悉自己的工作區域、學習餐廳系統并拿到自己的工作牌(相當于創建EventLoop并設置線程ID的過程)。

這時候,另一位經理(其他線程)已經開始在前臺接待客人,并試圖將客人分配給"正在培訓中"的服務員。但問題是,這些服務員可能還沒有完成培訓,沒有拿到工作牌,甚至可能還沒有被正式雇傭!

那么我們需要保護這個指針指針變量的互斥與同步訪問,需要使用一個互斥鎖和一個條件變量來保證指針的安全。

在餐廳中,解決方案也是使用一個協調板和一個明確的流程:

  • 招聘和培訓完全結束后,才將服務員的信息放到協調板上
  • 前臺經理只查看協調板上已確認可用的服務員
  • 使用一個信號系統(如專門的管理員)確保協調板的更新和查看不會同時發生

類的設計

綜上我們知道了EventLoop的流程

  • 首先,EventLoopThread對象被創建(可能在主線程或其他線程中)
  • 當調用EventLoopThread的構造方法時,它會創建一個新的線程
  • 這個新線程開始執行StartRoutine()函數
  • 在StartRoutine()函數內部,線程創建一個新的EventLoop對象
  • 線程將這個EventLoop對象的指針安全地賦值給共享變量_loop
  • 通知等待的線程EventLoop已經創建完成
  • 開始運行EventLoop的事件循環

那么EventLoopThread類的成員如下:

class EventLoopThread
{
private:EventLoop* _loop;std::thread _thread;std::mutex _mutex;      //保護_loop安全std::condition_variable _cond;  //實現同步
private:
//線程的入口函數void StartRoutine();
public: EventLoopThread(){}//提供一個接口用于獲取內部的EventLoop//意味著這個_loop會被多個線程競爭,那么需要鎖和條件變量來實現同步互斥//因為未來線程剛創建的時候,在還沒有創建好EventLoop對象的時候,這時候就可能會被主線程或者其他線程來獲取Loop了,那么這時候是線程不安全的,所以需要加鎖保護//同時,為了防止線程中的EventLoop對象還沒創建就有線程來獲取,我們需要再使用一個條件變量。 申請到鎖之后,如果條件不滿足,線程還需要在條件變量下等待,直到條件滿足再來競爭鎖并獲取鎖EventLoop* GetEventLoop();
};

模塊的實現

入口函數很簡單,無非就是加鎖創建完EventLoop對象之后,喚醒在條件變量下等待的線程,然后就開始執行EventLoop的Start循環邏輯。

私有接口

    //線程的入口函數void StartRoutine(){//加鎖創建對象EventLoop* loop = new EventLoop();{std::unique_lock<std::mutex> lock(_mutex);_loop = loop;}//喚醒條件變量下的線程_cond.notify_all();//啟動EventLoop循環loop -> Start();}

公有接口?

然后就是構造函數,無非就是初始化_loop和設置thread的入口函數

    EventLoopThread():_loop(nullptr),_thread(std::bind(&EventLoopThread::StartRoutine, this)) // 創建一個新線程,并指定StartRoutine作為線程的入口函數{}

剩下的就是一個獲取EventLoop的接口了,其實無非就是加鎖和條件變量下等待這兩個步驟:

    EventLoop* GetEventLoop(){EventLoop* ret = nullptr;{std::unique_lock<std::mutex> lock(_mutex); //加鎖_cond.wait(lock,[&](){return _loop!=nullptr;}); //判斷函數返回值為真//走到這里說明被喚醒了ret = _loop;}return ret;}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/899994.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/899994.shtml
英文地址,請注明出處:http://en.pswp.cn/news/899994.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

UE5學習筆記 FPS游戲制作38 繼承標準UI

文章目錄 UE的UIUMG的繼承繼承標準控件創建標準控件繼承標準控件的用處 UE的UI 和Untiy有onGui和UGui類似&#xff0c;UE有slateUI和UMG,slateUI是早期只能用C編寫的UI&#xff0c;UMG是現在使用的&#xff0c;可以拖拽編輯的UI slateUI是UMG的父類 UMG的繼承 我們編寫一個控…

C#核心學習(七)面向對象--封裝(6)C#中的拓展方法與運算符重載: 讓代碼更“聰明”的魔法

目錄 一、什么是拓展方法&#xff1f; 二、拓展方法有啥用&#xff1f;怎么寫拓展方法&#xff1f; 1. ?核心用途 2. ?編寫步驟 實現步驟 關鍵點說明 關鍵規則 3. ?注意事項 三、什么是運算符重載&#xff1f; 四、運算符重載有啥用&#xff1f;怎么寫&#xff1f;…

銀行卡歸屬地查詢API接口如何對接?

銀行卡歸屬地查詢 API 接口是一種能讓開發者通過編程方式獲取銀行卡歸屬地等相關信息的工具。借助此接口&#xff0c;開發者可將銀行卡歸屬地查詢功能集成到自己的應用程序或系統里&#xff0c;像電商平臺、第三方支付公司等都能運用它來提升業務的準確性與安全性。 銀行卡歸屬…

ORM mybits mybits-plus

ORM ORM 即對象關系映射&#xff08;Object Relational Mapping&#xff09;&#xff0c;是一種程序設計技術&#xff0c;用于實現面向對象編程語言里不同類型系統的數據之間的轉換。下面從基本概念、工作原理、優勢與劣勢、常見的 ORM 框架等方面詳細介紹 ORM。 常見的orm框架…

網絡編程—網絡概念

目錄 1 網絡分類 1.1 局域網 1.2 廣域網 2 常見網絡概念 2.1 交換機 2.2 路由器 2.3 集線器 2.4 IP地址 2.5 端口號 2.6 協議 3 網絡協議模型 3.1 OSI七層模型 3.2 TCP/IP五層模型 3.3 每層中常見的協議和作用 3.3.1 應用層 3.3.2 傳輸層 3.3.3 網絡層 3.3.4…

4月3日工作日志

一個樸實無華的目錄 今日學習內容&#xff1a;1.關系數據庫 今日學習內容&#xff1a; 1.關系數據庫

git commit Message 插件解釋說明

- feat - 一項新功能 - fix - 一個錯誤修復 - docs - 僅文檔更改 - style - 不影響代碼含義的更改&#xff08;空白、格式化、缺少分號等&#xff09; - refactor - 既不修復錯誤也不添加功能的代碼更改 - perf - 提高性能的代碼更改 - build - 影響構建系統或外部依賴項…

ngx_open_file

定義在 src\os\unix\ngx_files.h #define ngx_open_file(name, mode, create, access) \open((const char *) name, mode|create, access) name&#xff1a;文件名&#xff08;通常是一個字符串&#xff09;。mode&#xff1a;文件打開模式&#x…

23種設計模式-行為型模式-責任鏈

文章目錄 簡介問題解決代碼核心改進點&#xff1a; 總結 簡介 責任鏈是一種行為設計模式&#xff0c;允許你把請求沿著處理者鏈進行發送。收到請求后&#xff0c;每個處理者均可對請求進行處理&#xff0c;或將其傳遞給鏈上的下個處理者。 問題 假如你正在開發一個訂單系統。…

注意力機制在大語言模型中的原理與實現總結

注意力機制在大語言模型中的原理與實現總結 1. 章節介紹 在大語言模型的學習中&#xff0c;理解注意力機制至關重要。本章節旨在深入剖析注意力機制的原理及其在大語言模型中的應用&#xff0c;為構建和優化大語言模型提供理論與實踐基礎。通過回顧神經網絡基礎及傳統架構的局…

kafka消息可靠性傳輸語義

Kafka提供了多種消息傳遞語義&#xff0c;以適應不同的業務需求和可靠性要求。以下是Kafka消息傳輸的可靠性語義及其實現機制&#xff1a; 1. At Most Once&#xff08;至多一次&#xff09; 語義&#xff1a;消息可能會丟失&#xff0c;但不會被重復傳遞。 實現機制&#xf…

NLP高頻面試題(三十三)——Vision Transformer(ViT)模型架構介紹

Transformer架構在自然語言處理領域取得了顯著成功&#xff0c;激發了研究人員將其應用于計算機視覺任務的興趣。Vision Transformer&#xff08;ViT&#xff09;應運而生&#xff0c;成為圖像分類等視覺任務中的新興架構。本文將介紹ViT的基本架構、工作原理&#xff0c;并與傳…

Oracle數據庫數據編程SQL<3.6 PL/SQL 包(Package)>

包是Oracle數據庫中一種重要的PL/SQL程序結構,它將邏輯相關的變量、常量、游標、異常、過程和函數組織在一起,提供了更好的封裝性和模塊化。在大型項目中,可能有很多模塊,而每一個模塊又有自己的存過、函數等。而這些存過、函數默認是放在一起的,如果所有的存過函數都是放…

機器學習 分類算法

【實驗名稱】 實驗&#xff1a;分類算法 【實驗目的】 1.了解分類算法理論基礎 2.平臺實現算法 3. 編程實現分類算法 【實驗原理】 分類(Categorization or Classification)就是按照某種標準給對象貼標簽(label),再根據標簽來區分歸類。 【實驗環境】 OS&#xff1a;Ubuntu16.0…

HTML5 Canvas繪畫板項目實戰:打造一個功能豐富的在線畫板

HTML5 Canvas繪畫板項目實戰&#xff1a;打造一個功能豐富的在線畫板 這里寫目錄標題 HTML5 Canvas繪畫板項目實戰&#xff1a;打造一個功能豐富的在線畫板項目介紹技術棧核心功能實現1. 畫板初始化與工具管理2. 多樣化繪畫工具3. 事件處理機制 技術要點分析1. Canvas上下文優化…

【YOLOv8】YOLOv8改進系列(12)----替換主干網絡之StarNet

主頁&#xff1a;HABUO&#x1f341;主頁&#xff1a;HABUO &#x1f341;YOLOv8入門改進專欄&#x1f341; &#x1f341;如果再也不能見到你&#xff0c;祝你早安&#xff0c;午安&#xff0c;晚安&#x1f341; 【YOLOv8改進系列】&#xff1a; YOLOv8結構解讀 YOLOv8…

1Panel 面板 寶塔面板 Ubuntu 24.04

1Panel 面板 寶塔面板 Ubuntu 24.04 https://1panel.cn/ 1Panel 是一款開源的 Linux 服務器運維管理面板&#xff0c;它就像是給服務器配上了一個智能管家&#xff0c;讓我們能通過 Web 端輕松管理服務器。以往我們管理 Linux 服務器&#xff0c;常常需要在命令行中輸入各種復…

Node.js全局生效的中間件

目錄 1. 目錄結構 2. 代碼實現 2.1 安裝Express 2.2 app.js - 主文件 2.3 globalMiddleware.js - 全局中間件 3. 程序運行結果 4. 總結 在Node.js的Express框架中&#xff0c;全局生效的中間件是指應用程序啟動后&#xff0c;對所有請求都有效的中間件。它通常用于日志記…

WiFi(無線局域網)技術的多種工作模式

WiFi&#xff08;無線局域網&#xff09;技術支持多種工作模式&#xff0c;以滿足不同的網絡需求和應用場景。以下是主要的WiFi工作模式及其詳細說明&#xff1a; 1. 基礎設施模式&#xff08;Infrastructure Mode&#xff09; [無線接入點 (AP)]/ | \ [客戶端…

PHP 8.x:現代Web開發的性能與效率革命

隨著PHP 8.x系列的持續演進&#xff0c;這門誕生于1995年的“古老”語言正煥發新生。通過引入革命性的JIT編譯器、類型系統增強及一系列現代化語法特性&#xff0c;PHP 8.x不僅鞏固了其在Web開發領域的統治地位&#xff0c;更將性能與開發者體驗推向新高度。 一、JIT編譯器&am…