在多线程编程中,同步是一个至关重要的概念,它确保了程序在执行共享资源访问时的正确性和一致性。
锁(Locks)是实现线程同步的基本工具之一,C++11 在<mutex>
头文件中提供了多种锁类型和相关操作。
下面我们将介绍 C++11 线程中的几种锁及其使用方法。
std::mutex
std::mutex
是最基本的互斥锁,它提供了独占的锁定机制,确保同一时间只有一个线程可以访问被保护的资源。
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx; // 定义互斥量
void print_thread_id(int id) {
mtx.lock(); // 上锁
std::cout << "Thread #" << id << '\n';
mtx.unlock(); // 解锁
}
int main() {
std::thread threads[10];
for (int i = 0; i < 10; ++i)
threads[i] = std::thread(print_thread_id, i + 1);
for (auto& th : threads) th.join();
return 0;
}
输出:
Thread #9
Thread #5
Thread #2
Thread #4
Thread #10
Thread #3
Thread #1
std::recursive_mutex
std::recursive_mutex
允许同一个线程多次获得同一个互斥量。这对于递归函数访问被保护资源非常有用。
#include <iostream>
#include <thread>
#include <mutex>
std::recursive_mutex rec_mtx; // 定义可递归互斥量
int counter = 0;
void recursive_access(int id, int level) {
rec_mtx.lock(); // 上锁
if (level < 5) {
std::cout << "Thread #" << id << ": level " << level << '\n';
recursive_access(id, level + 1);
}
rec_mtx.unlock(); // 解锁
}
int main() {
std::thread t1(recursive_access, 1, 0);
std::thread t2(recursive_access, 2, 0);
t1.join();
t2.join();
return 0;
}
输出:
Thread #1: level 3
Thread #1: level 4
Thread #2: level 0
Thread #2: level 1
Thread #2: level 2
Thread #2: level 3
Thread #2: level 4
std::lock_guard
std::lock_guard
是一个作用域锁,提供了RAII(Resource Acquisition Is Initialization)风格的锁管理。它在构造时自动上锁,在析构时自动解锁,适用于简单的场景。
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx; // 定义互斥量
void print_thread_id(int id) {
std::lock_guard<std::mutex> lck(mtx); // 自动上锁
std::cout << "Thread #" << id << '\n';
// 自动解锁
}
int main() {
std::thread threads[10];
for (int i = 0; i < 10; ++i)
threads[i] = std::thread(print_thread_id, i + 1);
for (auto& th : threads) th.join();
return 0;
}
输出:
Thread #7
Thread #8
Thread #9
Thread #10
Thread #1
Thread #3
Thread #6
std::unique_lock
std::unique_lock
是一种更灵活的锁,与std::lock_guard
类似,也提供RAII风格的锁管理,但它允许延迟锁定(deferred locking)、提前解锁(unlocking before the end of the scope)、以及通过条件变量等待(waiting on condition variables)。
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <thread>
#include <vector>
std::mutex mtx;
std::condition_variable cv;
std::queue<int> data_queue;
void producer(int id) {
int data = id * 42; // 简单地使用id乘以42作为数据
{
std::unique_lock<std::mutex> lck(mtx);
data_queue.push(data);
std::cout << "Producer " << id << " added " << data << " to queue\n";
cv.notify_one(); // 通知等待的消费者线程
}
}
void consumer(int id) {
std::unique_lock<std::mutex> lck(mtx);
cv.wait(lck, []{ return !data_queue.empty(); }); // 等待直到data_queue非空
int data = data_queue.front();
data_queue.pop();
std::cout << "Consumer " << id << " processed " << data << std::endl;
}
int main() {
std::vector<std::thread> producers;
std::vector<std::thread> consumers;
// 创建生产者线程
for (int i = 0; i < 5; ++i) {
producers.push_back(std::thread(producer, i + 1));
}
// 创建消费者线程
for (int i = 0; i < 5; ++i) {
consumers.push_back(std::thread(consumer, i + 1));
}
// 等待所有生产者线程完成
for (auto& t : producers) {
t.join();
}
// 等待所有消费者线程完成
for (auto& t : consumers) {
t.join();
}
return 0;
}
输出:
Producer 5 added 210 to queue
Consumer 2 processed 210
Producer 1 added 42 to queue
Consumer 1 processed 42
Producer 4 added 168 to queue
Consumer 4 processed 168
Producer 3 added 126 to queue
Consumer 5 processed 126
Producer 2 added 84 to queue
Consumer 3 processed 84
总结
C++11 的多线程库为开发者提供的线程同步工具,包括各种类型的锁。今天介绍的是最简单、最常用的互斥锁,最后一个示例用到了条件变量和它的通知机制,这个我后面文章会介绍其用法。
通过上述示例,我们可以看到如何在实际编程中利用这些锁来保护共享数据,避免数据竞争和死锁等问题,确保程序的正确性和稳定性。