17 互斥量与条件变量
在上一篇中,我们讨论了多线程编程的基本概念,包括线程的创建、同步以及一些简单的线程调度。在这一篇中,我们将深入探讨多线程编程中至关重要的两种同步机制:互斥量
(Mutex)和条件变量
(Condition Variable)。这两者是确保多个线程安全高效地共享资源的关键工具。
互斥量(Mutex)
互斥量
是一种用于保护共享资源的同步机制。它确保在任意时刻只有一个线程可以访问某个资源,从而避免了并发访问时出现的数据竞争或不一致性问题。
1. 创建和使用互斥量
在 C++ 中,我们可以使用 std::mutex
类来创建互斥量。以下是一个基本示例,展示了如何创建一个互斥量并在多个线程之间安全地访问共享变量。
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx; // 创建互斥量
int shared_data = 0;
void increment() {
for (int i = 0; i < 1000; ++i) {
mtx.lock(); // 上锁
++shared_data; // 访问共享数据
mtx.unlock(); // 解锁
}
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << "Final shared data: " << shared_data << std::endl;
return 0;
}
在这个例子中,两个线程 t1
和 t2
都在增量 shared_data
。使用 mtx.lock()
和 mtx.unlock()
来确保在对 shared_data
进行操作时,不会有其他线程同时访问。
2. RAII 和 std::lock_guard
直接使用 lock()
和 unlock()
是容易出错的,如果在上锁后发生异常,那么互斥量将不会被解锁。为了避免这种情况,C++11 提供了 std::lock_guard
类,它可以帮助我们确保在作用域结束时自动释放互斥量:
void increment() {
for (int i = 0; i < 1000; ++i) {
std::lock_guard<std::mutex> lock(mtx); // RAII 风格上锁
++shared_data;
}
}
这样,无论函数如何退出,互斥量都会被安全地解锁。
条件变量(Condition Variable)
条件变量
是一种同步机制,用于通知一个或多个线程某个条件已经发生。它通常与互斥量结合使用,以实现线程间的等待和通知。
1. 创建和使用条件变量
条件变量的基本使用建构在 std::condition_variable
上。以下是一个生产者-消费者的例子,使用条件变量来协调生产者和消费者的工作。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
std::mutex mtx;
std::condition_variable cv;
std::queue<int> buffer;
const unsigned int max_buffer_size = 10;
void producer() {
for (int i = 0; i < 20; ++i) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [] { return buffer.size() < max_buffer_size; }); // 等待条件
buffer.push(i);
std::cout << "Produced: " << i << std::endl;
lock.unlock();
cv.notify_all(); // 通知消费者
}
}
void consumer() {
for (int i = 0; i < 20; ++i) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [] { return !buffer.empty(); }); // 等待条件
int value = buffer.front();
buffer.pop();
std::cout << "Consumed: " << value << std::endl;
lock.unlock();
cv.notify_all(); // 通知生产者
}
}
int main() {
std::thread prod(producer);
std::thread cons(consumer);
prod.join();
cons.join();
return 0;
}
在这个例子中,生产者
线程在 buffer
中放入数字,而消费者
线程则从 buffer
中取出数字。cv.wait(lock, condition)
用来使线程在条件不满足时挂起,并在条件满足时恢复执行。通过 cv.notify_all()
,生产者和消费者可以相互通知以继续执行。
总结
在多线程编程中,互斥量
和条件变量
是确保数据一致性和正确性的重要工具。互斥量
帮助我们保护共享数据的访问,而条件变量
则允许线程之间进行有效的通信。在实际编程中,合理使用这些工具可以使得我们的程序更安全、更高效。
在下一篇中,我们将继续探讨如何设计线程安全的数据结构
,并介绍如何在多线程环境中安全地使用这些数据结构。