在上一篇中,我们讨论了多线程编程的基本概念,包括线程的创建、同步以及一些简单的线程调度。在这一篇中,我们将深入探讨多线程编程中至关重要的两种同步机制:互斥量
(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; }
|
在这个例子中,两个线程 t1
和 t2
都在增量 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); ++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()
,生产者和消费者可以相互通知以继续执行。
总结
在多线程编程中,互斥量
和条件变量
是确保数据一致性和正确性的重要工具。互斥量
帮助我们保护共享数据的访问,而条件变量
则允许线程之间进行有效的通信。在实际编程中,合理使用这些工具可以使得我们的程序更安全、更高效。
在下一篇中,我们将继续探讨如何设计线程安全的数据结构
,并介绍如何在多线程环境中安全地使用这些数据结构。