C++笔记之信号量、互斥量与PV操作

C++笔记之信号量、互斥量与PV操作

文章目录

  • C++笔记之信号量、互斥量与PV操作
    • 1.信号量概念
    • 2.信号量例程一
    • 3.信号量例程二
    • 4.信号量例程三
    • 5.互斥量
    • 6.PV操作概念
    • 7.PV操作详解——抄自:https://mp.weixin.qq.com/s/vvjhbzsWQNRkU7-b_dURlQ
    • 8.PV操作的英文全称

1.信号量概念

C++中的信号量是一种同步原语,用于在多线程或多进程环境中管理资源的访问和控制并发访问的方式。信号量主要用于协调不同线程或进程之间对共享资源的访问,以确保互斥性和同步性。

信号量有两种常见的类型:二进制信号量和计数信号量。

  1. 二进制信号量(Binary Semaphore):也称为互斥锁(Mutex),它只能取两个值,通常是0和1。它用于实现互斥访问,即同一时间只允许一个线程或进程访问共享资源。当一个线程或进程获得了二进制信号量,其他尝试获取的线程或进程将被阻塞,直到信号量被释放。

  2. 计数信号量(Counting Semaphore):计数信号量可以取多个值,通常是非负整数。它用于控制同时访问共享资源的数量,允许多个线程或进程访问资源,但可以限制并发访问的数量。线程或进程可以等待信号量的计数增加,以获得访问权限,或者通过释放信号量来减少计数。

信号量通常具有两个主要操作:

  • Wait(等待)操作:线程或进程尝试获取信号量。如果信号量的计数不满足要求(例如,计数为0),则线程或进程将被阻塞,直到条件满足。
  • Signal(通知)操作:线程或进程释放信号量,增加计数。这通常是在使用完共享资源后执行的操作,以通知其他等待的线程或进程。

信号量是多线程和多进程编程中重要的同步工具,用于避免竞态条件和确保数据的一致性。在C++中,你可以使用标准库提供的互斥锁、条件变量以及其他同步原语来实现信号量,或者使用第三方库中提供的信号量实现,如Boost C++库中的信号量。

2.信号量例程一

在这里插入图片描述

运行
在这里插入图片描述

代码

#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>class Semaphore {public:Semaphore(int count = 0) : count_(count) {}void notify() {std::unique_lock<std::mutex> lock(mutex_);count_++;cv_.notify_one();}void wait() {std::unique_lock<std::mutex> lock(mutex_);while (count_ == 0) {cv_.wait(lock);}count_--;}private:std::mutex mutex_;std::condition_variable cv_;int count_;
};int main() {Semaphore semaphore(0); // 创建一个初始计数为3的信号量std::thread t1([&semaphore]() {semaphore.wait();std::cout << "Thread 1 is running." << std::endl;});std::thread t2([&semaphore]() {semaphore.wait();std::cout << "Thread 2 is running." << std::endl;});semaphore.notify(); // 释放一个许可std::this_thread::sleep_for(std::chrono::seconds(1));std::cout << " 让主线程等待一会儿..." << std::endl;semaphore.notify(); // 释放一个许可t1.join();t2.join();return 0;
}

3.信号量例程二

在这里插入图片描述

运行
在这里插入图片描述

代码

#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>class Semaphore {public:Semaphore(int count = 0) : count_(count) {}void notify() {std::unique_lock<std::mutex> lock(mutex_);count_++;cv_.notify_one();}void wait() {std::unique_lock<std::mutex> lock(mutex_);while (count_ == 0) {cv_.wait(lock);}count_--;}private:std::mutex mutex_;std::condition_variable cv_;int count_;
};Semaphore sem(0); // 创建一个初始计数为0的信号量void worker(int id) {std::cout << "Thread " << id << " is waiting." << std::endl;sem.wait();std::cout << "Thread " << id << " has acquired the semaphore." << std::endl;// 这里可以执行需要互斥访问的代码
}int main() {std::thread t1(worker, 1);std::thread t2(worker, 2);std::this_thread::sleep_for(std::chrono::seconds(2)); // 让主线程等待一会儿std::cout << "Main thread is notifying the semaphore." << std::endl;sem.notify(); // 释放一个许可t1.join();t2.join();return 0;
}

4.信号量例程三

这个示例模拟了一个生产者-消费者问题,其中多个生产者线程和消费者线程共享一个有界缓冲区,信号量用于控制对缓冲区的并发访问。

在此示例中,有三个生产者线程和三个消费者线程,它们共享一个有界缓冲区。Semaphore类用于控制缓冲区的空闲和满状态。生产者线程生成随机项目并将它们放入缓冲区,然后通知消费者线程。消费者线程从缓冲区中取出项目并通知生产者线程。信号量确保缓冲区在多线程环境中得到正确的访问和同步。

这个示例有助于理解信号量在多线程环境中的应用,尤其是在生产者-消费者问题中的作用。通过信号量,可以控制多个线程之间的并发访问,以避免数据竞态和确保正确的协调。

在这里插入图片描述

运行
在这里插入图片描述

代码

#include <condition_variable>
#include <iostream>
#include <mutex>
#include <queue>
#include <thread>
#include <vector>const int BUFFER_SIZE = 5;class Semaphore {public:Semaphore(int count = 0) : count_(count) {}void notify() {std::unique_lock<std::mutex> lock(mutex_);count_++;cv_.notify_one();}void wait() {std::unique_lock<std::mutex> lock(mutex_);while (count_ == 0) {cv_.wait(lock);}count_--;}private:std::mutex mutex_;std::condition_variable cv_;int count_;
};Semaphore empty(BUFFER_SIZE); // 空缓冲区的信号量
Semaphore full(0);            // 满缓冲区的信号量
std::mutex bufferMutex;       // 缓冲区互斥量
std::queue<int> buffer;       // 共享缓冲区void producer(int id) {for (int i = 0; i < 10; ++i) {int item = rand() % 100; // 随机生成一个项目empty.wait();            // 等待空缓冲区bufferMutex.lock();      // 锁定缓冲区buffer.push(item);       // 将项目放入缓冲区std::cout << "Producer " << id << " produced: " << item << std::endl;bufferMutex.unlock(); // 解锁缓冲区full.notify();        // 通知缓冲区已满std::this_thread::sleep_for(std::chrono::milliseconds(100));}
}void consumer(int id) {for (int i = 0; i < 10; ++i) {full.wait();        // 等待满缓冲区bufferMutex.lock(); // 锁定缓冲区int item = buffer.front();buffer.pop();std::cout << "Consumer " << id << " consumed: " << item << std::endl;bufferMutex.unlock(); // 解锁缓冲区empty.notify();       // 通知缓冲区已空std::this_thread::sleep_for(std::chrono::milliseconds(250));}
}int main() {std::vector<std::thread> producers;std::vector<std::thread> consumers;for (int i = 0; i < 3; ++i) {producers.emplace_back(producer, i);consumers.emplace_back(consumer, i);}for (auto &producerThread : producers) {producerThread.join();}for (auto &consumerThread : consumers) {consumerThread.join();}return 0;
}

5.互斥量

在这里插入图片描述

6.PV操作概念

C++中的PV操作通常是指与线程同步和互斥相关的操作,用于实现信号量机制。PV操作通常是Semaphore(信号量)的操作,用于控制多个线程对共享资源的访问。PV操作包括两个主要操作:

  1. P操作(等待操作):也称为down操作,用于获取信号量,并在信号量的值减一之前阻塞线程(如果信号量的值已经为0,则线程将被阻塞)。P操作通常用于锁定临界区,以防止多个线程同时访问共享资源。

    在C++中,可以使用std::mutexstd::unique_lock来实现P操作,也可以使用std::condition_variable来等待信号量的值达到某个条件。

    std::mutex mtx;
    std::unique_lock<std::mutex> lock(mtx);// 执行P操作,等待互斥锁
    lock.lock();
    // 访问共享资源
    // ...
    lock.unlock();
    
  2. V操作(释放操作):也称为up操作,用于释放信号量,并在信号量的值加一后唤醒一个或多个等待线程。V操作通常用于解锁临界区,以允许其他线程访问共享资源。

    在C++中,可以使用std::mutexstd::unique_lockstd::condition_variable来实现V操作。

    std::mutex mtx;
    std::unique_lock<std::mutex> lock(mtx);// 执行V操作,释放互斥锁
    lock.unlock();
    // ...
    

请注意,C++标准库还提供了一些高级的同步原语,如std::mutexstd::condition_variablestd::atomic,可以用于更灵活和安全地进行线程同步操作。此外,C++11之后引入的标准库还提供了std::thread来创建和管理线程,以及std::atomic用于原子操作,这些功能有助于更容易地编写多线程应用程序。

7.PV操作详解——抄自:https://mp.weixin.qq.com/s/vvjhbzsWQNRkU7-b_dURlQ

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

8.PV操作的英文全称

PV操作中,P和V通常代表以下内容的缩写:

  1. P:P表示"Produce"(生产)或"Post"(提交)。在某些上下文中,它表示请求或者申请资源的操作。在信号量(Semaphore)和互斥锁(Mutex)等并发编程中,P操作通常用于请求资源或者进入临界区。

  2. V:V表示"Vaporize"(释放)或"Vacate"(撤销)。在某些上下文中,它表示释放或者归还资源的操作。在并发编程中,V操作通常用于释放资源或者退出临界区。

这些缩写通常用于描述在并发编程中使用的信号量、互斥锁或其他同步机制中的两种基本操作,用于管理对共享资源的访问。不同的文献和编程环境可能会使用不同的术语来表示这些操作,但P和V是比较常见的缩写。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HTTPS建立连接的过程

HTTPS 协议是基于 TCP 协议的&#xff0c;因而要先建立 TCP 的连接。在这个例子中&#xff0c;TCP 的连接是在手机上的 App 和负载均衡器 SLB 之间的。 尽管中间要经过很多的路由器和交换机&#xff0c;但是 TCP 的连接是端到端的。TCP 这一层和更上层的 HTTPS 无法看到中间的包…

拼多多API接口的使用方针如下:

了解拼多多API接口 拼多多API接口是拼多多网提供的一种应用程序接口&#xff0c;允许开发者通过程序访问拼多多网站的数据和功能。通过拼多多API接口&#xff0c;开发者可以开发各种应用程序&#xff0c;如店铺管理工具、数据分析工具、购物比价工具等。在本章中&#xff0c;我…

Spring中的设计模式

目录 工厂模式 组合模式 适配器模式 代理模式 单例模式 观察者模式 模板方法模式 责任链模式 Spring有着非常优雅的设计&#xff0c;很多地方都遵循SOLID原则&#xff0c;里面的设计模式更是数不胜数大概有以下几种&#xff1a; 工厂模式 所谓的工厂模式&#xff0c;核…

在MySQL中使用!=还能走索引吗?

在MySQL中使用!还能走索引吗&#xff1f; 一般情况下&#xff0c;我们会在一个索引上较多的使用等值查询或者范围查询&#xff0c;此时索引大多可以帮助我们极快的查询出我们需要的数据。 那当我们在where条件中对索引列使用!查询&#xff0c;索引还能发挥他的作用吗&#xf…

vue3 中使用 echarts 图表——准备篇

我们常常在项目中使用图表来表示数据&#xff0c;而目前最常用的图标就是echarts&#xff0c;接下来我们就开始学习在vue中使用echarts图标。 一、准备一个vue项目&#xff08;一般通过vite来构建&#xff0c;而不是vue-cli&#xff09; 1.找到打开vite官网 2. 运行创建命令 …

英语字典的一些 关键字 解释:

1. specialized &#xff1a;专业术语 这里的specialized 后面还接了 finance & economics 含义是 在经济和金融的领域&#xff0c;principal 作为一个专业术语&#xff0c;含义是 “本金&#xff0c;可生息资本” 2. XXXXXX

Web1.0——Web2.0时代——Web3.0

Web1.0 Web1.0是互联网的早期阶段&#xff0c;也被称为个人电脑时代的互联网。在这个阶段&#xff0c;用户主要通过web浏览器从门户网站单向获取内容&#xff0c;进行浏览和搜索等操作。在这个时代&#xff0c;技术创新主导模式、基于点击流量的盈利共通点、门户合流、明晰的主…

Vue-1.8生命周期

Vue生命周期 一个Vue实例从创建到销毁的整个过程。 生命周期&#xff1a; 1&#xff09;创建&#xff1a;响应式数据 ->发送初始化渲染请求 2&#xff09;挂载&#xff1a;渲染数据->操作dom 3&#xff09;更新&#xff1a;数据修改&#xff0c;更新视图 4&#xf…

028.Python面向对象_类补充_元类

我 的 个 人 主 页&#xff1a;&#x1f449;&#x1f449; 失心疯的个人主页 &#x1f448;&#x1f448; 入 门 教 程 推 荐 &#xff1a;&#x1f449;&#x1f449; Python零基础入门教程合集 &#x1f448;&#x1f448; 虚 拟 环 境 搭 建 &#xff1a;&#x1f449;&…

【tomcat、java】

java&#xff1a;maven配置 1.安装插件 <build><plugins><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.1</version><configuration><port&…

一种重要的天然氨基酸L-Homopropargylglycine(HPG)|CAS:942518-19-6

产品简介&#xff1a;L-Homopropargylglycine是一种重要的天然氨基酸&#xff0c;具有多种生物活性和医学应用价值。它广泛应用于生物学、药学、化学等多个领域。在生物学中&#xff0c;HPG被用作蛋白质合成的标记物&#xff0c;可以通过其特殊的化学反应与蛋白质中的半胱氨酸残…

【SV中的多线程fork...join/join_any/join_none】

SV中fork_join/fork_join_any/fork_join_none 1 一目了然1.1 fork...join1.2 fork...join_any1.3 fork...join_none 2 总结 SV中fork_join和fork_join_any和fork_join_none; Note: fork_join在Verilog中也有&#xff0c;只有其他的两个是SV中独有的&#xff1b; 1 一目了然 1.…

操作系统 OS

本文章是学习《操作系统》慕课版 和 王道《2024年 操作系统 考研复习指导》后所做的笔记&#xff0c;其中一些图片来源于学习资料。 目录 概念&#xff08;定义&#xff09; 目标 方便性 有效性 可扩充性 开放性 作用 OS 作为用户与计算机硬件系统之间的接口 — 人机交…

仅用61行代码,你也能从零训练大模型

本文并非基于微调训练模型&#xff0c;而是从头开始训练出一个全新的大语言模型的硬核教程。看完本篇&#xff0c;你将了解训练出一个大模型的环境准备、数据准备&#xff0c;生成分词&#xff0c;模型训练、测试模型等环节分别需要做什么。AI 小白友好~文中代码可以直接实操运…

Puppeteer基础知识(一)

Puppeteer基础知识&#xff08;一&#xff09; Puppeteer基础知识&#xff08;一&#xff09;一、简介二、其他一些自动化测试工具三、安装与使用四、Puppeteer常用命令五、常见问题解决&#xff1a; 一、简介 Puppeteer 是一个强大而灵活的工具&#xff0c;可以用于网页爬虫、…

Transformer预测 | Pytorch实现基于Transformer 的锂电池寿命预测(CALCE数据集)

文章目录 效果一览文章概述模型描述程序设计参考资料效果一览 文章概述 Pytorch实现基于Transformer 的锂电池寿命预测,环境为pytorch 1.8.0,pandas 0.24.2 随着充放电次数的增加,锂电池的性能逐渐下降。电池的性能可以用容量来表示,故寿命预测 (RUL) 可以定义如下: SOH(t…

RTC 时间、闹钟

实时时钟RTC是一个独立的定时器。RTC模块拥有一个连续计数的计数器&#xff0c;在软件配置下&#xff0c;可以提供时钟日历的功能。修改计数器的值可以重新设置当前时间和日期 RTC还包含用于管理低功耗模式的自动唤醒单元。 在掉电情况下 RTC仍可以独立运行 只要芯片的备用电源…

实现协议互通:探索钡铼BL124EC的EtherCAT转Ethernet/IP功能

钡铼BL124EC是一种用于工业网络通信的网关设备&#xff0c;专门用于将EtherCAT协议转换成Ethernet/IP协议。它充当一个桥梁&#xff0c;连接了使用不同协议的设备&#xff0c;使它们能够无缝地进行通信和互操作。 具体来说&#xff0c;BL124EC通过支持EtherCAT&#xff08;以太…

2023,全网最真实的自动化测试学习路线,看不懂来打我!

随着测试行业的发展&#xff0c;“会代码”越来越成为测试工程师的一个标签。打开各大招聘网站&#xff0c;测试工程师月薪一万以上基本都有一个必备技能&#xff0c;那就是自动化测试。那么自动化测试到底难不难呢&#xff1f;下面我将会将我的经历讲给大家听&#xff0c;希望…

【java基础学习】之DOS命令

#java基础学习 1.常用的DOS命令&#xff1a; dir:列出当前目录下的文件以及文件夹 md: 创建目录 rd:删除目录cd:进入指定目录 cd.. :退回到上级目录 cd\ : 退回到根目录 del:删除文件 exit:退出dos命令行 1.dir:列出当前目录下的文件以及文件夹 2.md: 创建目录 …