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

news/2025/2/16 4:58:42/

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.ppmy.cn/news/1137882.html

相关文章

python 打包可执行文件-pyinstaller详解

python 打包可执行文件-pyinstaller详解 引言一、参数详解二、优化代码三、体积压缩 引言 pyinstaller是一个将python程序打包成独立可执行文件&#xff08;exe&#xff0c;app等&#xff09;的工具&#xff0c;它具有跨平台兼容性&#xff0c;可以在windows&#xff0c;mac和…

C++11各种锁的具体使用

1.什么是互斥量&#xff08;锁&#xff09;&#xff1f; 互斥量在实际开发中很常用&#xff0c;需要学习了解&#xff01; 这样比喻&#xff1a;单位上有一台打印机&#xff08;共享数据a&#xff09;&#xff0c;你要用打印机&#xff08;线程1要操作数据a&#xff09;&#…

项目设计:YOLOv5目标检测+机构光相机(intel d455和d435i)测距

1.介绍 1.1 Intel D455 Intel D455 是一款基于结构光&#xff08;Structured Light&#xff09;技术的深度相机。 与ToF相机不同&#xff0c;结构光相机使用另一种方法来获取物体的深度信息。它通过投射可视光谱中的红外结构光图案&#xff0c;然后从被拍摄物体表面反射回来…

packihx: aborting after 3 lines.

packihx: aborting after 3 lines.

数据结构:二叉树(超详解析)

目录​​​​​​​ 1.树概念及结构 1.1树的概念 1.2树的相关概念 1.3树的表示 1.3.1孩子兄弟表示法&#xff1a; 1.3.2双亲表示法&#xff1a;只存储双亲的下标或指针 两节点不在同一树上&#xff1a; 2.二叉树概念及结构 2.1.概念 2.2.特殊的二叉树&#xff1a; 2…

php实战案例记录(15)获取GET和POST请求参数

在PHP中&#xff0c;可以使用$_GET和$_POST超全局变量来获取GET和POST请求参数。 获取GET请求参数&#xff1a; 要获取GET请求参数&#xff0c;可以使用$_GET超全局变量。它是一个关联数组&#xff0c;其中键是参数的名称&#xff0c;值是参数的值。例如&#xff0c;如果URL是…

【Java每日一题】— —第二十二题:类名作参数进行方法调用的传递问题。(2023.10.06)

&#x1f578;️Hollow&#xff0c;各位小伙伴&#xff0c;今天我们要做的是第二十二题。 &#x1f3af;问题&#xff1a; 类名作参数进行方法调用的传递问题。 形式参数的问题&#xff1a; &#xff08;1&#xff09;基本类型&#xff1a;形式参数的改变不影响实际参数。实参应…

蓝桥等考Python组别十三级003

第一部分:选择题 1、Python L13 (15分) 运行下面程序,输出的结果是( )。 t = (1, 2, 2, 1, 4, 3, 2) print(t.count(2)) 1234正确答案:C 2、Python L13 (