C++笔记之信号量、互斥量与PV操作
文章目录
- C++笔记之信号量、互斥量与PV操作
- 1.信号量概念
- 2.信号量例程一
- 3.信号量例程二
- 4.信号量例程三
- 5.互斥量
- 6.PV操作概念
- 7.PV操作详解——抄自:https://mp.weixin.qq.com/s/vvjhbzsWQNRkU7-b_dURlQ
1.信号量概念
C++中的信号量是一种同步原语,用于在多线程或多进程环境中管理资源的访问和控制并发访问的方式。信号量主要用于协调不同线程或进程之间对共享资源的访问,以确保互斥性和同步性。
信号量有两种常见的类型:二进制信号量和计数信号量。
-
二进制信号量(Binary Semaphore):也称为互斥锁(Mutex),它只能取两个值,通常是0和1。它用于实现互斥访问,即同一时间只允许一个线程或进程访问共享资源。当一个线程或进程获得了二进制信号量,其他尝试获取的线程或进程将被阻塞,直到信号量被释放。
-
计数信号量(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操作包括两个主要操作:
-
P操作(等待操作):也称为down操作,用于获取信号量,并在信号量的值减一之前阻塞线程(如果信号量的值已经为0,则线程将被阻塞)。P操作通常用于锁定临界区,以防止多个线程同时访问共享资源。
在C++中,可以使用
std::mutex
或std::unique_lock
来实现P操作,也可以使用std::condition_variable
来等待信号量的值达到某个条件。std::mutex mtx; std::unique_lock<std::mutex> lock(mtx); // 执行P操作,等待互斥锁 lock.lock(); // 访问共享资源 // ... lock.unlock();
-
V操作(释放操作):也称为up操作,用于释放信号量,并在信号量的值加一后唤醒一个或多个等待线程。V操作通常用于解锁临界区,以允许其他线程访问共享资源。
在C++中,可以使用
std::mutex
、std::unique_lock
或std::condition_variable
来实现V操作。std::mutex mtx; std::unique_lock<std::mutex> lock(mtx); // 执行V操作,释放互斥锁 lock.unlock(); // ...
请注意,C++标准库还提供了一些高级的同步原语,如std::mutex
、std::condition_variable
和std::atomic
,可以用于更灵活和安全地进行线程同步操作。此外,C++11之后引入的标准库还提供了std::thread
来创建和管理线程,以及std::atomic
用于原子操作,这些功能有助于更容易地编写多线程应用程序。