17 互斥量与条件变量

在上一篇中,我们讨论了多线程编程的基本概念,包括线程的创建、同步以及一些简单的线程调度。在这一篇中,我们将深入探讨多线程编程中至关重要的两种同步机制:互斥量(Mutex)和条件变量(Condition Variable)。这两者是确保多个线程安全高效地共享资源的关键工具。

互斥量(Mutex)

互斥量是一种用于保护共享资源的同步机制。它确保在任意时刻只有一个线程可以访问某个资源,从而避免了并发访问时出现的数据竞争或不一致性问题。

1. 创建和使用互斥量

在 C++ 中,我们可以使用 std::mutex 类来创建互斥量。以下是一个基本示例,展示了如何创建一个互斥量并在多个线程之间安全地访问共享变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#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;
}

在这个例子中,两个线程 t1t2 都在增量 shared_data。使用 mtx.lock()mtx.unlock() 来确保在对 shared_data 进行操作时,不会有其他线程同时访问。

2. RAII 和 std::lock_guard

直接使用 lock()unlock() 是容易出错的,如果在上锁后发生异常,那么互斥量将不会被解锁。为了避免这种情况,C++11 提供了 std::lock_guard 类,它可以帮助我们确保在作用域结束时自动释放互斥量:

1
2
3
4
5
6
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 上。以下是一个生产者-消费者的例子,使用条件变量来协调生产者和消费者的工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#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(),生产者和消费者可以相互通知以继续执行。

总结

在多线程编程中,互斥量条件变量是确保数据一致性和正确性的重要工具。互斥量帮助我们保护共享数据的访问,而条件变量则允许线程之间进行有效的通信。在实际编程中,合理使用这些工具可以使得我们的程序更安全、更高效。

在下一篇中,我们将继续探讨如何设计线程安全的数据结构,并介绍如何在多线程环境中安全地使用这些数据结构。

17 互斥量与条件变量

https://zglg.work/c-plusplus-one/17/

作者

IT教程网(郭震)

发布于

2024-08-10

更新于

2024-08-22

许可协议

分享转发

交流

更多教程加公众号

更多教程加公众号

加入星球获取PDF

加入星球获取PDF

打卡评论