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.hqwc.cn/news/127251.html

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

相关文章

【一、灵犀考试系统项目设计、框架搭建】

一、创建数据库 1、打开power designer&#xff0c;新建数据库模型 2、新建数据表&#xff0c;以及关系 【注意】 图片的类型有两种&#xff1a;varbinary 和 image varbinary : 二进制字节流&#xff0c;可以自动控制长度 image : 最大可放2G图片 3、创建数据库&#…

FastThreadLocal 快在哪里 ?

FastThreadLocal 快在哪里 &#xff1f; 引言FastThreadLocalset如何获取当前线程私有的InternalThreadLocalMap &#xff1f;如何知道当前线程使用到了哪些FastThreadLocal实例 ? get垃圾回收 小结 引言 FastThreadLocal 是 Netty 中造的一个轮子&#xff0c;那么为什么放着…

Doris 2.0.1 DockerFile版 升级实战

1、Doris 2.0.1 DockerFile 的制作 参考 Doris 2.0.1 Dockerfile制作-CSDN博客 2、之前的Doris 集群通过 Docker容器进行的部署&#xff0c;需提前准备好Doris2.0.1的镜像包 参考&#xff1a; 集群升级 - Apache Doris Doris 升级请遵守不要跨两个及以上关键节点版本升级的…

RabbitMQ之Fanout(扇形) Exchange解读

目录 基本介绍 适用场景 springboot代码演示 演示架构 工程概述 RabbitConfig配置类&#xff1a;创建队列及交换机并进行绑定 MessageService业务类&#xff1a;发送消息及接收消息 主启动类RabbitMq01Application&#xff1a;实现ApplicationRunner接口 基本介绍 Fa…

JDBC-day02(使用PreparedStatement实现CRUD操作)

所需的数据库数据要导入到自己的数据库库中 三&#xff1a;使用PreparedStatement实现CRUD操作 数据库连接被用于向数据库服务器发送命令和 SQL 语句&#xff0c;并接受数据库服务器返回的结果。其实一个数据库连接就是一个Socket连接。CRUD操作&#xff1a;根据返回值的有无…

李宏毅 2022机器学习 HW3 boss baseline 上分记录

作业数据是所有数据都有标签的版本。 李宏毅 2022机器学习 HW3 boss baseline 上分记录 1. 训练数据增强2. cross validation&ensemble3. test dataset augmentation4. resnet 1. 训练数据增强 结论&#xff1a;训练数据增强、更长时间的训练、dropout都证明很有效果&…

QT内存管理

Qt的半自动化的内存管理 &#xff08;1&#xff09;QObject及其派生类的对象&#xff0c;如果其parent非0&#xff0c;那么其parent析构时会析构该对象。 &#xff08;2&#xff09;QWidget及其派生类的对象&#xff0c;可以设置 Qt::WA_DeleteOnClose 标志位(当close时会析构…

小谈设计模式(16)—抽象工厂模式

小谈设计模式&#xff08;16&#xff09;—抽象工厂模式 专栏介绍专栏地址专栏介绍 抽象工厂模式结构抽象工厂&#xff08;AbstractFactory&#xff09;具体工厂&#xff08;ConcreteFactory&#xff09;抽象产品&#xff08;AbstractProduct&#xff09;具体产品&#xff08;C…

Postgresql中的C/C++混编(JIT)

1 Postgresql编译JIT 整体上看使用了GCC、G编译文件&#xff0c;最后用G汇总&#xff1a; GCC编译的三个.o文件llvmjit、llvmjit_deform、llvmjit_expr llvmjit.c -> llvmjit.o gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -…

Java中栈实现怎么选?Stack、Deque、ArrayDeque、LinkedList(含常用Api积累)

目录 Java中的Stack类 不用Stack有以下两点原因 1、从性能上来说应该使用Deque代替Stack。 2、Stack从Vector继承是个历史遗留问题&#xff0c;JDK官方已建议优先使用Deque的实现类来代替Stack。 该用ArrayDeque还是LinkedList&#xff1f; ArrayDeque与LinkList区别&#xff1…

强化学习------Sarsa算法

简介 SARSA&#xff08;State-Action-Reward-State-Action&#xff09;是一个学习马尔可夫决策过程策略的算法&#xff0c;通常应用于机器学习和强化学习学习领域中。它由Rummery 和 Niranjan在技术论文“Modified Connectionist Q-Learning&#xff08;MCQL&#xff09;” 中…

Mysql——创建数据库,对表的创建及字段定义、数据录入、字段增加及删除、重命名表。

一.创建数据库 create database db_classics default charsetutf8mb4;//创建数据库 use db_classics;//使用该数据库二.对表的创建及字段定义 create table if not exists t_hero ( id int primary key auto_increment, Name varchar(100) not null unique, Nickname varchar(1…