信号量Semaphore

什么是信号量?

C++中的信号量(Semaphore)是一种同步对象,用于控制对共享资源的访问,以防止多个线程或进程同时访问同一资源,从而避免数据不一致的问题。信号量通过维护一个计数值来实现这一功能,该计数值表示可以同时访问共享资源的线程或进程的数量。当一个线程或进程想要访问共享资源时,它会尝试减少信号量的计数值;如果计数值大于零,线程或进程可以继续执行,否则它必须等待直到信号量的计数值变为正数。当线程或进程完成对共享资源的访问后,它会释放信号量,即增加计数值,从而可能允许其他正在等待的线程或进程继续执行。

信号量可以干什么?

  1. 互斥
  2. 限制并发访问
  3. 线程同步
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <condition_variable>


class Semaphore {
public:
    Semaphore(int count = 1) : count_(count) {}

    void notify() {
        std::unique_lock<std::mutex> lock(mutex_);
        ++count_;
        condition_.notify_one();
    }

    void wait() {
        std::unique_lock<std::mutex> lock(mutex_);
        while (count_ <= 0) {
            condition_.wait(lock);
        }
        --count_;
    }

private:
    std::mutex mutex_;
    std::condition_variable condition_;
    int count_;
};

// 共享资源
int sharedResource = 0;

void workerFunction(Semaphore& semaphore) {
    for (int i = 0; i < 1000; ++i) {
        semaphore.wait(); // 加锁
        ++sharedResource;
        semaphore.notify(); // 解锁
    }
}

//实现互斥

int main() {
    Semaphore semaphore(1);
    std::vector<std::thread> threads;

    // 创建 10 个工作线程
    for (int i = 0; i < 10; ++i) {
        threads.push_back(std::thread(workerFunction, std::ref(semaphore)));
    }

    // 等待所有线程完成
    for (auto& thread : threads) {
        thread.join();
    }

    std::cout << "Shared resource value: " << sharedResource << std::endl;

    return 0;
}
#include <iostream>
#include <thread>
#include <vector>
#include <chrono>
#include <mutex>
#include <condition_variable>

class Semaphore {
public:
    Semaphore(int count = 1) : count_(count) {}

    void notify() {
        std::unique_lock<std::mutex> lock(mutex_);
        ++count_;
        condition_.notify_one();
    }

    void wait() {
        std::unique_lock<std::mutex> lock(mutex_);
        while (count_ <= 0) {
            condition_.wait(lock);
        }
        --count_;
    }

private:
    std::mutex mutex_;
    std::condition_variable condition_;
    int count_;
};

void workerFunction(int threadId, Semaphore& semaphore) {
    semaphore.wait(); // 获取访问权限
    std::cout << "Thread " << threadId << " is accessing the shared resource." << std::endl;
    // 模拟访问共享资源
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Thread " << threadId << " has finished accessing the shared resource." << std::endl;
    semaphore.notify(); // 释放访问权限
}

//限制并发访问

int main() {
    Semaphore semaphore(3); // 允许最多3个线程同时访问
    std::vector<std::thread> threads;

    // 创建10个工作线程
    for (int i = 0; i < 10; ++i) {
        threads.push_back(std::thread(workerFunction, i, std::ref(semaphore)));
    }

    // 等待所有线程完成
    for (auto& thread : threads) {
        thread.join();
    }

    return 0;
}
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <condition_variable>

class Semaphore {
public:
    Semaphore(int count = 1) : count_(count) {}

    void notify() {
        std::unique_lock<std::mutex> lock(mutex_);
        ++count_;
        condition_.notify_one();
    }

    void wait() {
        std::unique_lock<std::mutex> lock(mutex_);
        while (count_ <= 0) {
            condition_.wait(lock);
        }
        --count_;
    }

private:
    std::mutex mutex_;
    std::condition_variable condition_;
    int count_;
};

void task1(Semaphore& semaphore1, Semaphore& semaphore2) {
    std::cout << "Task 1 is executing." << std::endl;
    // 模拟任务执行
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Task 1 is completed." << std::endl;
    // 通知任务2可以开始执行
    semaphore1.notify();
    // 等待任务2完成
   // semaphore2.wait();
}

void task2(Semaphore& semaphore1, Semaphore& semaphore2) {
    // 等待任务1完成
    semaphore1.wait();
    std::cout << "Task 2 is executing." << std::endl;
    // 模拟任务执行
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Task 2 is completed." << std::endl;
    // 通知任务3可以开始执行
    semaphore2.notify();
}

void task3(Semaphore& semaphore2) {
    // 等待任务2完成
    semaphore2.wait();
    std::cout << "Task 3 is executing." << std::endl;
    // 模拟任务执行
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Task 3 is completed." << std::endl;
}

//线程同步

int main() {
    Semaphore semaphore1(0), semaphore2(0);

    std::thread t1(task1, std::ref(semaphore1), std::ref(semaphore2));
    std::thread t2(task2, std::ref(semaphore1), std::ref(semaphore2));
    std::thread t3(task3, std::ref(semaphore2));

    t1.join();
    t2.join();
    t3.join();

    return 0;
}

信号量怎么实现?

C++11

使用互斥锁和条件变量模拟信号量。

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>

class Semaphore {
public:
    Semaphore(int count) : _count(count) {}

    void acquire() {
        std::unique_lock<std::mutex> lock(_mutex);
        while (_count == 0) {
            _cv.wait(lock);
        }
        --_count;
    }

    void release() {
        std::lock_guard<std::mutex> lock(_mutex);
        ++_count;
        _cv.notify_one();
    }

private:
    int _count;
    std::mutex _mutex;
    std::condition_variable _cv;
};

Semaphore taskSemaphore(4); // 最大并发数为4

void processTask(int taskID) {
    taskSemaphore.acquire();

    std::cout << "Task " << taskID << " started." << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(10));
    std::cout << "Task " << taskID << " finished." << std::endl;

    taskSemaphore.release();
}

int main() {
    std::vector<std::thread> threads;
    const int numTasks = 10;

    for (int i = 1; i <= numTasks; ++i) {
        threads.emplace_back(processTask, i);
    }

    for (auto& thread : threads) {
        thread.join();
    }

    return 0;
}

C++20

使用std::counting_semaphore

#include <iostream>
#include <thread>
#include <semaphore>
#include <vector>

// 假设我们有一个最大并发数为4的任务队列
std::counting_semaphore<4> taskSemaphore(4);

void processTask(int taskID) {
    // 请求一个任务许可
    taskSemaphore.acquire();

    std::cout << "Task " << taskID << " started." << std::endl;
    // 这里模拟任务处理时间
    std::this_thread::sleep_for(std::chrono::milliseconds(10000));
    std::cout << "Task " << taskID << " finished." << std::endl;

    // 任务处理完释放许可
    taskSemaphore.release();
}

int main() {
    std::vector<std::thread> threads;
    const int numTasks = 10;

    for (int i = 1; i <= numTasks; ++i) {
        threads.emplace_back(processTask, i);
    }

    for (auto& thread : threads) {
        thread.join();
    }

    return 0;
}

经典生产者消费者问题

#include <iostream>
#include <thread>
#include <vector>
#include <queue>
#include <chrono>
#include <mutex>
#include <condition_variable>

class Semaphore {
public:
    Semaphore(int count = 1) : count_(count) {}

    void notify() {
        std::unique_lock<std::mutex> lock(mutex_);
        ++count_;
        condition_.notify_one();
    }

    void wait() {
        std::unique_lock<std::mutex> lock(mutex_);
        while (count_ <= 0) {
            condition_.wait(lock);
        }
        --count_;
    }

private:
    std::mutex mutex_;
    std::condition_variable condition_;
    int count_;
};

const int BUFFER_SIZE = 10;
std::queue<int> buffer;
std::mutex mutex_; // 互斥锁,用于保护共享数据
Semaphore empty(BUFFER_SIZE), full(0);

void producer(int producerId) {
    for (int i = 0; i < 100; ++i) {
        empty.wait(); // 等待缓冲区有空位
        std::unique_lock<std::mutex> lock(mutex_);
        buffer.push(i);
        std::cout << "Producer " << producerId << " produced: " << i << std::endl;
        lock.unlock();
        full.notify(); // 通知消费者有新数据
    }
}

void consumer(int consumerId) {
    for (int i = 0; i < 100; ++i) {
        full.wait(); // 等待缓冲区有数据
        std::unique_lock<std::mutex> lock(mutex_);
        int data = buffer.front();
        buffer.pop();
        std::cout << "Consumer " << consumerId << " consumed: " << data << std::endl;
        lock.unlock();
        empty.notify(); // 通知生产者有空位
    }
}

int main() {
    std::vector<std::thread> producers, consumers;

    // 创建3个生产者线程
    for (int i = 0; i < 3; ++i) {
        producers.push_back(std::thread(producer, i));
    }

    // 创建3个消费者线程
    for (int i = 0; i < 3; ++i) {
        consumers.push_back(std::thread(consumer, i));
    }

    // 等待所有生产者线程完成
    for (auto& producer : producers) {
        producer.join();
    }

    // 等待所有消费者线程完成
    for (auto& consumer : consumers) {
        consumer.join();
    }

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/558173.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

设计模式之模板方法模式详解(下)

3&#xff09;钩子方法的使用 1.概述 钩子方法的引入使得子类可以控制父类的行为。 2.结构图 3.代码实现 将公共方法和框架代码放在抽象父类中 abstract class DataViewer {//抽象方法&#xff1a;获取数据public abstract void GetData();//具体方法&#xff1a;转换数据…

每日一题 — 最小覆盖子串

76. 最小覆盖子串 - 力扣&#xff08;LeetCode&#xff09; 解法一&#xff1a;暴力遍历哈希表 解法二&#xff1a;滑动窗口哈希表 定义left和right初始化为零&#xff0c;固定left&#xff0c;先向右遍历right&#xff0c;放到哈希表中这个时候我们需要统计有效字符的个数&…

深入挖掘C语言 ---- 文件操作

目录 1. 文件的打开和关闭1.1 流和标准流1.1.1流1.1.2标准流 1.2 文件指针1.3 文件的打开和关闭 2. 顺序读写3. 随机读写3.1 fseek3.2 ftell3.3 rewind 4. 读取结束判定 正文开始 1. 文件的打开和关闭 1.1 流和标准流 1.1.1流 我们程序的数据需要输出到各种外部设备, 也需要…

Leetcode算法训练日记 | day30

一、重新安排行程 1.题目 Leetcode&#xff1a;第 332 题 给你一份航线列表 tickets &#xff0c;其中 tickets[i] [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。 所有这些机票都属于一个从 JFK&#xff08;肯尼迪国际机场&#xff09;出发…

java算法day2

螺旋矩阵搜索插入位置查找元素第一个位置和最后一个位置 螺旋矩阵 解法&#xff1a;模拟&#xff0c;核心在于你怎么转&#xff0c;还有就是处理边界&#xff0c;边界如何收缩&#xff0c;什么时候停止旋转。最内圈的时候怎么处理。 通过上图的模拟来解决这个问题&#xff1a;…

数据库锁等待排查方法、命令行安装数据库及授权文件更新

欢迎关注“数据库运维之道”公众号&#xff0c;一起学习数据库技术! 本期将为大家分享“数据库锁等待排查方法、命令行安装数据库及授权文件更新”的运维技能。 关键词&#xff1a;锁等待、V$LOCK、V$TRXWAIT、死锁、锁超时、命令行部署达梦、授权文件更新 当用户反馈执行SQL语…

1985-2022年各地级市专利申请数据

1985-2022年各地级市专利申请数据 1、时间&#xff1a;1985-2022年 2、指标&#xff1a;行政区划代码、地区、省份、城市、年份、发明公布&#xff08;申请数&#xff09;、其中&#xff1a;获得授权、外观设计申请量、实用新型申请量 3、来源&#xff1a;国家知识产权局 4…

【Java】简单实现图书管理系统

前言 在本篇博客当中&#xff0c;我们会使用Java基础语法来简单实现一个图书管理系统&#xff0c;主要用到的知识为&#xff1a;封装、多态、继承、接口等等&#xff0c;并不会使用数据库来存储数据&#xff0c;请注意 需求 1. 要求设置管理员和普通用户两种身份&#xff0c…

【深度学习实战(9)】三种保存和加载模型的方式

一、state_dict方式&#xff08;推荐&#xff09; torch.save(model.state_dict(), PATH)model YourModel() model.load_state_dict(torch.load(PATH)) model.eval()记住一定要使用model.eval()来固定dropout和归一化层&#xff0c;否则每次推理会生成不同的结果。 二、整个…

实验室三大常用仪器3---交流毫伏表的使用方法(笔记)

目录 函数信号发生器、示波器、交流毫伏表如果连接 交流毫伏表的使用方法 测量值的读数问题 实验室三大常用仪器1---示波器的基本使用方法&#xff08;笔记&#xff09;-CSDN博客 实验室三大常用仪器2---函数信号发生器的基本使用方法&#xff08;笔记&#xff09;-CSDN博客…

C#自定义窗体更换皮肤的方法:创建特殊窗体

目录 1.窗体更换皮肤 2.实例 &#xff08;1&#xff09;图片资源管理器Resources.Designer.cs设计 &#xff08;2&#xff09;Form1.Designer.cs设计 &#xff08;3&#xff09;Form1.cs设计 &#xff08;4&#xff09; 生成效果 &#xff08;5&#xff09;一个遗憾 1.窗…

Git常见命令行操作和IDEA图形化界面操作

设置Git用户名和标签 在安装完Git以后需要设置用户和签名&#xff0c;至于为什么要设置用户签名可以看一下这篇文章【学了就忘】Git基础 — 11.配置Git用户签名说明 - 简书 (jianshu.com) 基本语法&#xff1a; git config --global user.name 用户名 git config --global u…

SpringBoot项目创建及简单使用

目录 一.SpringBoot项目 1.1SpringBoot的介绍 1.2SpringBoot优点 二.SpringBoot项目的创建 三.注意点 一.SpringBoot项目 1.1SpringBoot的介绍 Spring是为了简化Java程序而开发的&#xff0c;那么SpringBoot则是为了简化Spring程序的。 Spring 框架&#xff1a; Spring…

ARM之栈与方法

ARM之栈与方法 计算机中的栈是一种线性表&#xff0c;它被限定只能在一端进行插入和删除操作&#xff08;先进后出&#xff09;。通常将可以插入和删除操作的一端称为栈顶&#xff0c;相对的一端为栈底。 通常栈有递增堆栈&#xff08;向高地址方向生长&#xff09;、递减堆栈…

鸿蒙OpenHarmony【搭建Ubuntu环境】

搭建Ubuntu环境 在嵌入式开发中&#xff0c;很多开发者习惯于使用Windows进行代码的编辑&#xff0c;比如使用Windows的Visual Studio Code进行OpenHarmony代码的开发。但当前阶段&#xff0c;大部分的开发板源码还不支持在Windows环境下进行编译&#xff0c;如Hi3861、Hi3516…

Day37 IO流的操作

Day37 IO流的操作 文章目录 Day37 IO流的操作Java的文件拷贝利用 文件字节输出流 向文件写入数据利用 文件字节输入流 读取文件里的数据利用 带缓冲区的字节输出流 向文件写入数据利用 带有缓冲区的字节输入流 读取文件里的数据利用 字符输出转换流 向文件写入数据利用 字符输入…

Java全套智慧校园系统源码springboot+elmentui +Quartz可视化校园管理平台系统源码 建设智慧校园的5大关键技术

Java全套智慧校园系统源码springbootelmentui Quartz可视化校园管理平台系统源码 建设智慧校园的5大关键技术 智慧校园指的是以物联网为基础的智慧化的校园工作、学习和生活一体化环境&#xff0c;这个一体化环境以各种应用服务系统为载体&#xff0c;将教学、科研、管理和校园…

豆瓣影评信息爬取 (爬虫)

代码块&#xff1a; from lxml import etree import requestsheaders{User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0 }url_list[] for i in range(0,5):i*20urlsf"https:…

day02-新增员工

day01 新增员工业务逻辑整理 EmployeeController.java PostMappingApiOperation("新增员工")public Result save(RequestBody EmployeeDTO employeeDTO){System.out.println("当前线程的ID:" Thread.currentThread().getId());log.info("新增员工&a…

[leetcode] 56. 合并区间

文章目录 题目描述解题方法排序java代码复杂度分析 题目描述 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区…
最新文章