【Linux】從互斥原理到C++ RAII封裝實踐

📢博客主頁:https://blog.csdn.net/2301_779549673
📢歡迎點贊 👍 收藏 ?留言 📝 如有錯誤敬請指正!
📢本文由 JohnKi 原創,首發于 CSDN🙉
📢未來很長,值得我們全力奔赴更美好的生活?

在這里插入圖片描述

在這里插入圖片描述

文章目錄

  • 📢前言
  • 🏳??🌈一、從場景看互斥:為什么需要鎖?
  • 🏳??🌈二、互斥鎖核心概念圖解
    • 2.1 臨界區與非臨界區
    • 2. 2 進程線程間的互斥相關背景概念
  • 🏳??🌈三、Linux互斥鎖原理剖析
    • 3.2 初始化互斥量
    • 3.3 銷毀互斥量
    • 3.3 pthread_mutex 底層實現
  • 🏳??🌈四、C++ RAII封裝實戰
    • 4.1 基礎互斥類(Mutex)
    • 4.2 守衛鎖(LockGuard)
  • 🏳??🌈五、完整代碼
    • 5.1 Mutex.hpp
    • 5.2 Mutex.cc
    • 5.3 Makefile
  • 👥總結


📢前言

緊接上回的線程C++封裝,這回筆者著重介紹一下互斥的原理和其必要性,并手把手使用C++封裝一個RAII模型。

還有一點,筆者之后的封裝都會使用之前博客中封裝好的容器,需要的可以去倉庫或者前面的博客中自取。

RAII 的核心思想是將資源的獲取和初始化放在對象的構造函數中進行,而資源的釋放放在對象的析構函數中進行。當對象被創建時,其構造函數會自動執行,從而完成資源的獲取;當對象的生命周期結束時,其析構函數會被自動調用,從而完成資源的釋放。這樣,資源的生命周期就與對象的生命周期綁定在一起,利用 C++ 等語言的對象自動銷毀機制來確保資源的正確釋放。


🏳??🌈一、從場景看互斥:為什么需要鎖?

假設你的銀行賬戶余額是1000元,同時有兩個線程執行轉賬操作:

  • ?線程A:存入200元 → balance += 200
  • ?線程B:取出300元 → balance -= 300
    無鎖情況下可能的執行順序:
線程A讀取balance(1000) → 線程B讀取balance(1000) → 
線程A寫入1200 → 線程B寫入700  
最終結果:700元(正確應為900元)

🏳??🌈二、互斥鎖核心概念圖解

2.1 臨界區與非臨界區

在這里插入圖片描述

void* thread_func(void* arg) {// 非臨界區(可并發執行)prepare_data();  // 臨界區(需互斥訪問)pthread_mutex_lock(&mtx);update_shared_resource();  pthread_mutex_unlock(&mtx);// 非臨界區(可并發執行)post_process();
}

關鍵特征:

🔵 ?非臨界區:允許多線程并行(如圖中綠色區域)
🔴 ?臨界區:同一時刻僅一個線程執行(紅色區域)

2. 2 進程線程間的互斥相關背景概念

  • 臨界資源:多線程執行流共享的資源就叫做臨界資源
  • 臨界區:每個線程內部,訪問臨界資源的代碼,就叫做臨界區
  • 互斥:任何時刻,互斥保證有且只有一個執行流進入臨界區,訪問臨界資源,通常對臨界資源起保護作用
  • 原子性:(后面討論如何實現):不會被任何調度機制打斷的操作,該操作只有兩態,要么完成,要么未完成

🏳??🌈三、Linux互斥鎖原理剖析

核心操作流程:

sequenceDiagramparticipant 線程Aparticipant 互斥鎖participant 內核線程A->>互斥鎖: pthread_mutex_lock()alt 鎖空閑互斥鎖-->>線程A: 立即獲得鎖else 鎖被占線程A->>內核: 進入休眠隊列內核-->>線程A: 喚醒并獲取鎖end線程A->>臨界區: 執行操作線程A->>互斥鎖: pthread_mutex_unlock()

3.2 初始化互斥量

方法一:靜態分布

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER

方法二:動態分布

int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);

mutex:要初始化的互斥量
attr:NULL

3.3 銷毀互斥量

銷毀互斥量需要注意:

  • 使用 PTHREAD_MUTEXINITIALIZER 初始化的互斥量不需要銷毀
  • 不要銷毀一個已經加鎖的互斥量
  • 已經銷毀的互斥量,要確保后面不會有線程再嘗試加鎖
int pthread_mutex_destroy(pthread_mutex_t *mutex)

互斥量加鎖和解鎖

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
返回值:成功返回0,失敗返回錯誤號

調? pthread_mutex_lock 時,可能會遇到以下情況:

  • 互斥量處于未鎖狀態,該函數會將互斥量鎖定,同時返回成功
  • 發起函數調用時,其他線程已經鎖定互斥量,或者存在其他線程同時申請互斥量,但沒有競爭到互斥量,那么pthread lock調用會陷入阻塞(執行流被掛起),等待互斥量解鎖。

3.3 pthread_mutex 底層實現

// 鎖結構(簡化為x86實現)
struct pthread_mutex {int __lock;          // 鎖狀態標識int __count;         // 遞歸鎖計數器int __owner;         // 持有者線程IDint __kind;          // 鎖類型標識// ... 其他字段
};

🏳??🌈四、C++ RAII封裝實戰

4.1 基礎互斥類(Mutex)

// 互斥鎖封裝類(不可拷貝構造/賦值)class Mutex{public:// 禁止拷貝(保護系統鎖資源)Mutex(const Mutex&) = delete;const Mutex& operator = (const Mutex&) = delete;// 構造函數:初始化POSIX互斥鎖Mutex(){// 初始化互斥鎖屬性為默認值int n = ::pthread_mutex_init(&_lock, nullptr);(void)n; // 實際開發建議處理錯誤碼}// 析構函數:銷毀鎖資源~Mutex(){// 確保鎖已處于未鎖定狀態int n = ::pthread_mutex_destroy(&_lock);(void)n; // 生產環境應檢查返回值}// 加鎖操作(阻塞直至獲取鎖)void Lock(){// 可能返回EDEADLK(死鎖檢測)等錯誤碼int n = ::pthread_mutex_lock(&_lock);(void)n; // 簡化處理,實際建議拋異常或記錄日志}// 解鎖操作(必須由鎖持有者調用)void Unlock(){// 未持有鎖時解鎖將返回EPERMint n = ::pthread_mutex_unlock(&_lock);(void)n; }private:pthread_mutex_t _lock; // 底層鎖對象};

4.2 守衛鎖(LockGuard)

守衛鎖不是新的鎖類型,而是對已有鎖的自動化生命周期管理工具。這種設計模式完美契合圖示中"Lock-unlock"邊界需要嚴格匹配的核心訴求

守衛鎖工作流程

sequenceDiagramparticipant 線程participant 守衛鎖participant 互斥鎖線程->>守衛鎖: 創建LockGuard對象守衛鎖->>互斥鎖: 調用Lock()互斥鎖-->>守衛鎖: 獲得鎖線程->>臨界區: 執行操作線程->>守衛鎖: 對象離開作用域守衛鎖->>互斥鎖: 調用Unlock()互斥鎖-->>其他線程: 釋放鎖資源

實現

// RAII鎖守衛(自動管理鎖生命周期)class LockGuard{public:// 構造時加鎖(必須傳入已初始化的Mutex引用)LockGuard(Mutex &mtx):_mtx(mtx){_mtx.Lock(); // 進入臨界區}// 析構時自動解鎖(異常安全保證)~LockGuard(){_mtx.Unlock(); // 離開作用域自動釋放}private:Mutex &_mtx; // 引用方式持有,避免拷貝導致未定義行為};

🏳??🌈五、完整代碼

5.1 Mutex.hpp

#pragma once
#include <iostream>
#include <pthread.h> // POSIX線程庫頭文件namespace LockModule
{// 互斥鎖封裝類(不可拷貝構造/賦值)class Mutex{public:// 禁止拷貝(保護系統鎖資源)Mutex(const Mutex&) = delete;const Mutex& operator = (const Mutex&) = delete;// 構造函數:初始化POSIX互斥鎖Mutex(){// 初始化互斥鎖屬性為默認值int n = ::pthread_mutex_init(&_lock, nullptr);(void)n; // 實際開發建議處理錯誤碼}// 析構函數:銷毀鎖資源~Mutex(){// 確保鎖已處于未鎖定狀態int n = ::pthread_mutex_destroy(&_lock);(void)n; // 生產環境應檢查返回值}// 加鎖操作(阻塞直至獲取鎖)void Lock(){// 可能返回EDEADLK(死鎖檢測)等錯誤碼int n = ::pthread_mutex_lock(&_lock);(void)n; // 簡化處理,實際建議拋異常或記錄日志}// 解鎖操作(必須由鎖持有者調用)void Unlock(){// 未持有鎖時解鎖將返回EPERMint n = ::pthread_mutex_unlock(&_lock);(void)n; }private:pthread_mutex_t _lock; // 底層鎖對象};// RAII鎖守衛(自動管理鎖生命周期)class LockGuard{public:// 構造時加鎖(必須傳入已初始化的Mutex引用)LockGuard(Mutex &mtx):_mtx(mtx){_mtx.Lock(); // 進入臨界區}// 析構時自動解鎖(異常安全保證)~LockGuard(){_mtx.Unlock(); // 離開作用域自動釋放}private:Mutex &_mtx; // 引用方式持有,避免拷貝導致未定義行為};
}

5.2 Mutex.cc

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>int ticket = 0;
pthread_mutex_t mutex;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;void *route(void *arg)
{char *id = (char *)arg;while (1){pthread_mutex_lock(&mutex);if (ticket > 0){usleep(1000);printf("%s sells ticket:%d\n", id, ticket);ticket--;pthread_mutex_unlock(&mutex);}else{printf("%s wait on cond!\n", id);pthread_cond_wait(&cond, &mutex); //醒來的時候,會重新申請鎖!!printf("%s 被叫醒了\n", id);}pthread_mutex_unlock(&mutex);}return nullptr;
}int main(void)
{pthread_t t1, t2, t3, t4;pthread_mutex_init(&mutex, NULL);pthread_create(&t1, NULL, route, (void *)"thread 1");pthread_create(&t2, NULL, route, (void *)"thread 2");pthread_create(&t3, NULL, route, (void *)"thread 3");pthread_create(&t4, NULL, route, (void *)"thread 4");int cnt = 10;while(true){sleep(5);ticket += cnt;printf("主線程放票嘍, ticket: %d\n", ticket);pthread_cond_signal(&cond);}pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_join(t3, NULL);pthread_join(t4, NULL);pthread_mutex_destroy(&mutex);
}

5.3 Makefile

bin=testMutex
cc=g++
src=$(wildcard *.cc)
obj=$(src:.cc=.o)$(bin):$(obj)$(cc) -o $@ $^ -lpthread
%.o:%.cc$(cc) -c $< -std=c++17.PHONY:clean
clean:rm -f $(bin) $(obj).PHONY:test
test:echo $(src)echo $(obj)

👥總結

本篇博文對 從互斥原理到C++ RAII封裝實踐 做了一個較為詳細的介紹,不知道對你有沒有幫助呢

覺得博主寫得還不錯的三連支持下吧!會繼續努力的~

請添加圖片描述

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

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

相關文章

微服務無狀態服務設計

微服務無狀態服務設計是構建高可用、高擴展性系統的核心方法。 一、核心設計原則 請求獨立性 每個請求必須攜帶完整的上下文信息&#xff0c;服務不依賴本地存儲的會話或用戶數據。例如用戶認證通過JWT傳遞所有必要信息&#xff0c;而非依賴服務端Session。 狀態外置化 將會話…

30、map 和 unordered_map的區別和實現機制【高頻】

底層結構 map底層是紅黑樹結構&#xff0c;而unordered_map底層是哈希結構; 有序性 但是紅黑樹其實是一種二叉搜索樹&#xff0c;插入刪除時會自動排序hash因為是把數據映射到數組上的&#xff0c;而且存在哈希沖突&#xff0c;所以不能保證有序存儲 所以有序存儲使用map&a…

大數據-spark3.5安裝部署之local模式

spark&#xff0c;一個數據處理框架和計算引擎。 下載 local模式即本地模式&#xff0c;就是不需要任何其他節點資源就可以在本地執行spark代碼的環境。用于練習演示。 上傳解壓 使用PortX將文件上傳至/opt 進入/opt目錄&#xff0c;創建目錄module&#xff0c;解壓文件至/o…

Manus “Less structure,More intelligence ”獨行云端處理器

根據市場調研機構Statista數據顯示&#xff0c;全球的AR/AR的市場規模預計目前將達到2500億美元&#xff0c;Manus作為VR手套領域的領軍企業&#xff0c;足以顛覆你的認知。本篇文章將帶你解讀Manus產品&#xff0c;針對用戶提出的種種問題&#xff0c;Manus又將如何解決且讓使…

Oracle數據庫存儲結構--邏輯存儲結構

數據庫存儲結構&#xff1a;分為物理存儲結構和邏輯存儲結構。 物理存儲結構&#xff1a;操作系統層面如何組織和管理數據 邏輯存儲結構&#xff1a;Oracle數據庫內部數據組織和管理數據&#xff0c;數據庫管理系統層面如何組織和管理數據 Oracle邏輯存儲結構 數據庫的邏…

芯驛電子 ALINX 亮相德國紐倫堡,Embedded World 2025 精彩回顧

2025年3月13日&#xff0c;全球規模最大的嵌入式行業盛會——德國紐倫堡國際嵌入式展&#xff08;embedded world 2025&#xff09;圓滿落幕。 在這場匯聚全球 950 家展商、3 萬余專業觀眾的科技盛宴中&#xff0c;芯驛電子 ALINX 展位人頭攢動&#xff0c;多款尖端產品吸引客戶…

Nexus File類型Blob Stores遷移至Minio操作指南(上)

#作者&#xff1a;閆乾苓 文章目錄 目的前期準備查看file類型Blob Stores數據目錄位置aws cli客戶端連接工具OrientDB cli客戶端連接工具在minio中新建 bucket 目的 增強nexus構件數據的高可用性和擴展性 前期準備 查看并記錄需要遷移的Blob Store及repository 查看fil…

藍橋杯嵌入式組第十二屆省賽題目解析+STM32G431RBT6實現源碼

文章目錄 1.題目解析1.1 分而治之&#xff0c;藕斷絲連1.2 模塊化思維導圖1.3 模塊解析1.3.1 KEY模塊1.3.2 LED模塊1.3.3 LCD模塊1.3.4 TIM模塊1.3.5 UART模塊1.3.5.1 uart數據解析 2.源碼3.第十二屆題目 前言&#xff1a;STM32G431RBT6實現嵌入式組第十二屆題目解析源碼&#…

【MySQL】表的約束(上)

文章目錄 表的約束什么是表的約束空屬性默認值列描述&#xff08;comment&#xff09;零填充&#xff08;zerofill&#xff09;主鍵 總結 表的約束 什么是表的約束 表的約束&#xff08;Constraints&#xff09;是數據庫表中的規則&#xff0c;用于限制存儲的數據&#xff0c…

【Unity網絡同步框架 - Nakama研究(三)】

文章目錄 【Unity網絡同步框架 - Nakama研究(三)】準備工作前言Unity部分連接服務器創建并進入房間創建人物人物移動和同步 【Unity網絡同步框架 - Nakama研究(三)】 以下部分需要有一定的Unity基礎&#xff0c;在官方的案例Pirate Panic基礎上進行修改而成。如果沒有下載并熟悉…

前端存儲-indexdb封裝:dexie.js的使用

前言 indexedDB是一個用于在瀏覽器中存儲較大數據結構的Web API&#xff0c;并且提供了索引功能以實現高性能查找。dexie.js是對indexdb的封裝&#xff0c;前端用起來很方便。在此介紹一下項目中用到的操作語句&#xff0c;也方便記錄。我的項目是vue3項目。 開始 1、安裝 …

【AD】6-1 PCB常用規則

間距規則&#xff1a; 可自行修改線寬與間距&#xff08;默認10mil&#xff09; 線寬規則&#xff1a;電源線寬加粗 布線過程中更改線寬&#xff1a;走線狀態下&#xff0c;shiftw更改線寬&#xff0c;線寬要在規則范圍之內過孔規則&#xff1a; 阻焊規則&#xff1a;

MyBatis 的核心配置文件是干什么的? 它的結構是怎樣的? 哪些是必須配置的,哪些是可選的?

MyBatis 的核心配置文件&#xff08;通常命名為 mybatis-config.xml&#xff09;是 MyBatis 應用程序的入口點&#xff0c;它定義了 MyBatis 的全局配置信息 。 核心配置文件的作用&#xff1a; 配置 MyBatis 的運行時行為: 通過 <settings> 標簽設置全局參數&#xff…

搜廣推校招面經四十九

tiktok廣告算法 一、倒排索引原理及Map中Key的處理 具體使用方法見【搜廣推校招面經三十六】 倒排索引&#xff08;Inverted Index&#xff09;是信息檢索系統中常用的一種數據結構&#xff0c;用于快速查找包含某個關鍵詞的文檔。以下是倒排索引的原理及Map中Key的處理方式的…

【零基礎入門unity游戲開發——unity3D篇】3D物理系統之 —— 3D剛體組件Rigidbody

考慮到每個人基礎可能不一樣,且并不是所有人都有同時做2D、3D開發的需求,所以我把 【零基礎入門unity游戲開發】 分為成了C#篇、unity通用篇、unity3D篇、unity2D篇。 【C#篇】:主要講解C#的基礎語法,包括變量、數據類型、運算符、流程控制、面向對象等,適合沒有編程基礎的…

C# net deepseek RAG AI開發 全流程 介紹

deepseek本地部署教程及net開發對接 步驟詳解&#xff1a;安裝教程及net開發對接全流程介紹 DeepSeekRAG 中的 RAG&#xff0c;全稱是 Retrieval-Augmented Generation&#xff08;檢索增強生成&#xff09;&#xff0c;是一種結合外部知識庫檢索與大模型生成能力的技術架構。其…

用舊的手機搭建 MQTT Broker

MQTT Broker搭建 在Android上搭建MQTT所需工具: termux 通過網盤分享的文件:termux-app_v0.118.1+github-debug_armeabi-v7a.apk 鏈接: https://pan.baidu.com/s/1Iii2szXAc02cKVGdP1EuzQ?pwd=fqsc 提取碼: fqsc 在 Termux 中使用 MQTT(Message Queuing Telemetry Trans…

b站視頻下載工具軟件怎么下載

自行配置FFMPEG環境 請優先選擇批量下載&#xff0c;會自處理視頻和音頻文件。 如果要下載更高質量請登陸。 沒有配置FFMPEG下載后會有報錯提示&#xff0c;視頻音頻文件無法合并生成mp4文件 更新批量下載標題&#xff0c;只取視頻原標題&#xff0c;B站反爬機制登陸后下載多了…

# linux有哪些桌面環境?有哪些顯示服務器協議及顯示服務器?有哪些用于開發圖形用戶界面的工具包?

linux有哪些桌面環境&#xff1f;有哪些顯示服務器協議及顯示服務器&#xff1f;有哪些用于開發圖形用戶界面的工具包&#xff1f; 文章目錄 linux有哪些桌面環境&#xff1f;有哪些顯示服務器協議及顯示服務器&#xff1f;有哪些用于開發圖形用戶界面的工具包&#xff1f;1 顯…

Java 大視界 -- Java 大數據分布式計算中的資源調度與優化策略(131)

&#x1f496;親愛的朋友們&#xff0c;熱烈歡迎來到 青云交的博客&#xff01;能與諸位在此相逢&#xff0c;我倍感榮幸。在這飛速更迭的時代&#xff0c;我們都渴望一方心靈凈土&#xff0c;而 我的博客 正是這樣溫暖的所在。這里為你呈上趣味與實用兼具的知識&#xff0c;也…