C++17 信號量模擬實現

C++17 信號量模擬實現

一、實現原理

C++17 標準庫沒有原生信號量(C++20才有),但可以通過 std::mutex + std::condition_variable 模擬實現。以下是核心邏輯:

#include <mutex>
#include <condition_variable>class CountingSemaphore {
private:int count_;                  // 當前可用資源數std::mutex mutex_;           // 保護計數器的鎖std::condition_variable cv_; // 阻塞/喚醒線程public:explicit CountingSemaphore(int initial = 0) : count_(initial) {}// P操作:請求資源(計數器減1)void acquire() {std::unique_lock<std::mutex> lock(mutex_);cv_.wait(lock, [this] { return count_ > 0; }); // 等待資源可用--count_;}// V操作:釋放資源(計數器加1)void release() {std::lock_guard<std::mutex> lock(mutex_);++count_;cv_.notify_one(); // 喚醒一個等待線程}
};
二、完整可編譯代碼
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <chrono>// 信號量實現類
class CountingSemaphore {
private:int count_;std::mutex mutex_;std::condition_variable cv_;
public:explicit CountingSemaphore(int initial = 0) : count_(initial) {}void acquire() {std::unique_lock<std::mutex> lock(mutex_);cv_.wait(lock, [this] { return count_ > 0; });--count_;}void release() {std::lock_guard<std::mutex> lock(mutex_);++count_;cv_.notify_one();}
};// 全局資源
constexpr int MAX_BUFFER = 5;
CountingSemaphore empty_slots(MAX_BUFFER); // 初始空位
CountingSemaphore data_items(0);           // 初始數據項
std::mutex buffer_mutex;                   // 保護緩沖區
std::queue<int> buffer;                     // 共享緩沖區
bool producer_done = false;                // 生產結束標志// 生產者函數
void producer() {for (int i = 1; i <= 10; ++i) {empty_slots.acquire(); // 等待空位{std::lock_guard<std::mutex> lock(buffer_mutex);buffer.push(i);std::cout << "生產者添加: " << i << std::endl;}data_items.release(); // 增加數據項std::this_thread::sleep_for(std::chrono::milliseconds(100));}// 標記生產完成std::lock_guard<std::mutex> lock(buffer_mutex);producer_done = true;
}// 消費者函數
void consumer() {while (true) {data_items.acquire(); // 等待數據項{std::lock_guard<std::mutex> lock(buffer_mutex);if (producer_done && buffer.empty()) break; // 退出條件int val = buffer.front();buffer.pop();std::cout << "消費者取出: " << val << std::endl;}empty_slots.release(); // 釋放空位std::this_thread::sleep_for(std::chrono::milliseconds(150));}
}int main() {std::thread t1(producer);std::thread t2(consumer);t1.join();t2.join();std::cout << "程序正常退出" << std::endl;return 0;
}
三、編譯與運行
  1. 編譯命令(需要支持C++17的編譯器):

    g++ -std=c++17 -pthread -o semaphore_demo semaphore_demo.cpp
    
  2. 運行輸出示例

四、關鍵機制解析
組件作用
std::mutex保護共享計數器 count_ 的線程安全訪問
std::condition_variable實現阻塞等待和喚醒機制
cv_.wait()阻塞線程直到資源可用(count_ > 0
cv_.notify_one()資源釋放后喚醒一個等待線程
五、注意事項
  1. 虛假喚醒處理cv_.wait() 必須使用循環判斷條件

    cv_.wait(lock, [this] { return count_ > 0; }); // 正確寫法
    
  2. 死鎖避免:確保每次 acquire() 都有對應的 release()

  3. 性能優化:高頻場景下可改用 notify_all() + 線程競爭

    void release() {std::lock_guard<std::mutex> lock(mutex_);++count_;cv_.notify_all(); // 喚醒所有線程
    }
    

C語言的信號量

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>sem_t sem;
int shared_counter = 0;void* thread_func(void* arg) {for (int i = 0; i < 100000; i++) {sem_wait(&sem);       // P操作shared_counter++;     // 臨界區sem_post(&sem);       // V操作}return NULL;
}int main() {// 初始化信號量(初始值為1)if (sem_init(&sem, 0, 1) != 0) {perror("sem_init failed");return 1;}pthread_t t1, t2;// 創建線程1if (pthread_create(&t1, NULL, thread_func, NULL) != 0) {perror("pthread_create t1 failed");return 1;}// 創建線程2if (pthread_create(&t2, NULL, thread_func, NULL) != 0) {perror("pthread_create t2 failed");return 1;}// 等待線程結束pthread_join(t1, NULL);pthread_join(t2, NULL);printf("Final counter value: %d\n", shared_counter);  // 正確應為200000// 銷毀信號量sem_destroy(&sem);return 0;
}

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

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

相關文章

C++ 網絡層接口設計與實現:基于 Socket 編程

在網絡編程中&#xff0c;網絡層是 OSI 七層模型中負責將數據從源節點傳輸到目的節點的關鍵層次。在 C 中&#xff0c;網絡層的功能通常通過 Socket 編程接口來實現。Socket 提供了一種抽象機制&#xff0c;允許應用程序通過網絡發送和接收數據。本文將詳細介紹如何在 C 中使用…

uniapp中uni-easyinput 使用@input 不改變綁定的值

只允許輸入數字和字母 使用input 正則replace后賦值給A 遇到問題: 當輸入任意連續的非法字符時, 輸入框不變. 直到輸入一個合法字符非法字符才成功被過濾. <uni-forms-item label"納稅人識別號" name"number"><uni-easyinput v-model"numb…

Docker安裝hoppscotch

Docker安裝hoppscotch 1 簡介 1.1 Hoppscotch?系統 ?Hoppscotch?是一個輕量、高效的[API開發生態系統&#xff0c;開源于2020年&#xff0c;原名Postwoman&#xff0c;后更名為Hoppscotch。它基于Node.js構建&#xff0c;支持多種HTTP請求方法&#xff0c;包括GET、POST、…

1.Axum 與 Tokio:異步編程的完美結合

摘要 深入解析 Axum 核心架構與 Tokio 異步運行時的集成&#xff0c;掌握關鍵原理與實踐技巧。 一、引言 在當今的軟件開發領域&#xff0c;高并發和高性能是衡量一個系統優劣的重要指標。對于 Web 服務器而言&#xff0c;能夠高效地處理大量并發請求是至關重要的。Rust 語言…

CSS偽元素

偽元素 偽元素 用于在元素的內容前后或特定部分插入虛擬元素&#xff0c;并為其添加樣式&#xff0c;無需修改 HTML 結構。 語法&#xff1a;使用雙冒號 ::&#xff08;現代規范&#xff09; 以下是一些常見的CSS偽元素的示例&#xff1a; 1.::before &#xff1a; 在元素內…

easyexcel使用模板填充excel坑點總結

1.單層map設置值是{屬性}&#xff0c;那使用兩層map進行設置值&#xff0c;是不是可以使用{屬性.屬性}&#xff0c;以為取出map里字段只用{屬性}就可以設置值&#xff0c;那再加個.就可以從里邊map取出對應屬性&#xff0c;沒有兩層map寫法 填充得到的文件打開報錯 was empty (…

在Ubuntu服務器上部署xinference

一、拉取鏡像 docker pull xprobe/xinference:latest二、啟動容器&#xff08;GPU&#xff09; docker run -d --name xinference -e XINFERENCE_MODEL_SRCmodelscope -p 9997:9997 --gpus all xprobe/xinference:latest xinference-local -H 0.0.0.0 # 啟動一個新的Docker容…

三周年創作紀念日

文章目錄 回顧與收獲三年收獲的五個維度未來的展望致謝與呼喚 親愛的社區朋友們&#xff0c;大家好&#xff01; 今天是 2025 年 4 月 14 日&#xff0c;距離我在 2022 年 4 月 14 日發布第一篇技術博客《SonarQube 部署》整整 1,095 天。在這條創作之路上&#xff0c;我既感慨…

Redis——五種數據類型

目錄 前言 1.String 1.1RAW編碼 1.2EMBSTR編碼 1.3 INT編碼 2.List 3.Set 3.1 InSet編碼轉化成Dict編碼 4.ZSet 4.1結合SkipList和HT實現 4.2使用ZipList實現 4.3編碼轉換 4.4 ZipList排序功能 5.Hash 5.1Hash底層存儲結構 6.Redis數據結構和數據類型關系圖 前言…

zookeeper啟動報錯have small server identifier

解決方案&#xff1a; 1、查看myid是否有重復 2、查看server.X 與myid的X是否一致 3、啟動順序為myid從小到大的服務器順序

#Linux動態大小裁剪以及包大小變大排查思路

1 動態庫裁剪 庫分為動態庫和靜態庫&#xff0c;動態庫是在程序運行時才加載&#xff0c;靜態庫是在編譯時就加載到程序中。動態庫的大小通常比靜態庫小&#xff0c;因為動態庫只包含了程序需要的函數和數據&#xff0c;而靜態庫則包含了所有的函數和數據。靜態庫可以理解為引入…

消息隊列生產者投遞的高可靠性與一致性保障方案

在構建高可靠分布式系統時&#xff0c;確保業務數據庫與消息隊列&#xff08;MQ&#xff09;之間的一致性是一項核心挑戰。尤其當使用 Kafka 作為消息隊列中間件時&#xff0c;如何避免“數據庫寫入成功&#xff0c;但消息發送失敗”或“消息重復發送”等問題&#xff0c;成為系…

Formality:Bug記錄

相關閱讀 Formalityhttps://blog.csdn.net/weixin_45791458/category_12841971.html?spm1001.2014.3001.5482 本文記錄博主在使用Synopsys的形式驗證工具Formality中遇到的一個Bug。 Bug復現 情況一 // 例1 module dff (input clk, input d_in, output d_out …

通信算法之267 : DJI無人機 云哨 DroneID 640ms

DJI 無人機 與DroneID 轉 *** 載 0x01 摘要 消費級無人機可以用于高級航拍、物流和人道主義救援等等。但是其廣泛使用給安全、安保和隱私帶來了許多風險。例如&#xff0c;攻擊方可能會使用無人機進行監視、運輸非法物品&#xff0c;或通過侵入機場上方的封閉空域造成經濟損…

論壇測試報告

作者前言 &#x1f382; ??????&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ?&#x1f382; 作者介紹&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…

npx 的作用以及延伸知識(.bin目錄,npm run xx 執行)

文章目錄 前言原理解析1. npx 的作用2. 為什么會有 node_modules/.bin/lerna3. npx 的查找順序4. 執行流程總結1&#xff1a; 1. .bin 機制什么是 node_modules/.bin&#xff1f;例子 2. npx 的底層實現npx 是如何工作的&#xff1f;為什么推薦用 npx&#xff1f;npx 的特殊能力…

【c語言】深入理解指針3——回調函數

一、回調函數 回調函數&#xff1a;通過函數指針調用的函數. 當把一個函數的地址傳遞給另一個函數&#xff0c;通過該地址去調用其指向的函數&#xff0c;那么這個被調用的函數就是回調函數. 示例&#xff1a; 在【深入理解指針2】中結尾寫了用函數指針實現計算器的功能&#…

HTTP 核心概念

&#x1f9d1; 博主簡介&#xff1a;CSDN博客專家&#xff0c;歷代文學網&#xff08;PC端可以訪問&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移動端可微信小程序搜索“歷代文學”&#xff09;總架構師&#xff0c;15年工作經驗&#xff0c;…

VidBot:從野外 2D 人體視頻中學習可泛化的 3D 動作,實現零樣本機器人操控

25年3月來自慕尼黑工大、瑞士 ETH 和微軟的論文“VidBot: Learning Generalizable 3D Actions from In-the-Wild 2D Human Videos for Zero-Shot Robotic Manipulation”。 未來的機器人被設想為能夠執行各種家務的多功能系統。最大的問題仍然是&#xff0c;如何在盡量減少機器…

Linux 日常運維命令大全

Linux 作為一種開源操作系統&#xff0c;在服務器運維中扮演著重要角色。掌握常用的 Linux 命令對于運維人員而言至關重要。本文將整理一份 Linux 服務器運維常用命令大全&#xff0c;幫助你在日常工作中提高效率和準確性。 1. 基礎命令 基礎命令是Linux操作的起點&#xff0…