您的位置:首页 >Linux下C++如何处理多线程同步
发布于2026-05-02 阅读(0)
扫一扫,手机访问
在Linux平台上用C++搞多线程开发,线程同步是个绕不开的核心议题。处理不好,数据竞争、死锁这些“坑”随时可能出现。那么,有哪些趁手的同步工具可供选择呢?它们的典型用法又是怎样的?

下面,我们就来梳理几种C++标准库中常用的线程同步机制,并配上直观的代码示例。
这大概是大家最熟悉的同步原语了。它的职责非常明确:保护共享资源,确保同一时刻只有一个线程能访问它,相当于给临界区上了一把独占的锁。
#include
#include
#include
std::mutex mtx; // 定义一个互斥锁
void print_block(int n, char c) {
mtx.lock(); // 加锁
for (int i = 0; i < n; ++i) {
std::cout << c;
}
std::cout << '\n';
mtx.unlock(); // 解锁
}
int main() {
std::thread th1(print_block, 50, '*');
std::thread th2(print_block, 50, '$');
th1.join();
th2.join();
return 0;
}
看这段代码,两个线程都想往控制台打印字符。如果没有互斥锁,输出可能会混杂在一起。而mtx.lock()和mtx.unlock()这对操作,就保证了每个线程的打印区块是完整的。
普通互斥锁有个限制:同一个线程不能重复上锁,否则会立刻导致死锁。但有些场景下,我们可能需要函数递归调用,或者一个公共函数本身就需要加锁。这时候,递归互斥锁就派上用场了。
#include
#include
#include
std::recursive_mutex mtx; // 定义一个递归互斥锁
void print_block(int n, char c) {
mtx.lock(); // 加锁
if (n > 0) {
std::cout << c;
mtx.lock(); // 再次加锁
print_block(n - 1, c);
mtx.unlock(); // 解锁
}
std::cout << '\n';
mtx.unlock(); // 解锁
}
int main() {
std::thread th1(print_block, 50, '*');
std::thread th2(print_block, 50, '$');
th1.join();
th2.join();
return 0;
}
注意看,在递归函数print_block内部,同一个线程对mtx执行了多次lock()。如果换成普通的std::mutex,程序就会卡住。而std::recursive_mutex允许这种行为,只要确保加锁和解锁的次数匹配即可。
互斥锁解决了互斥访问的问题,但线程间有时需要“等待某个条件成立”。比如,一个线程要等队列非空才能取数据,另一个线程放入数据后需要通知它。这就是条件变量的典型应用场景。
#include
#include
#include
#include
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void print_id(int id) {
std::unique_lock lck(mtx);
cv.wait(lck, []{return ready;}); // 等待条件变量
std::cout << "Thread " << id << '\n';
}
void go() {
std::lock_guard lck(mtx);
ready = true;
cv.notify_all(); // 通知所有等待的线程
}
int main() {
std::thread threads[10];
for (int i = 0; i < 10; ++i) {
threads[i] = std::thread(print_id, i);
}
std::this_thread::sleep_for(std::chrono::seconds(1)); // 等待一段时间
go(); // 通知线程开始执行
for (auto& th : threads) {
th.join();
}
return 0;
}
这个例子展示了经典的“等待-通知”模式。10个线程启动后都在条件变量cv上等待,直到主线程调用go()将ready置为true并发出notify_all()信号,所有等待的线程才继续执行。cv.wait()的第二个参数是一个谓词(lambda表达式),它避免了虚假唤醒,是更健壮的写法。
对于“读多写少”的共享数据,使用互斥锁会限制并发性能,因为读操作之间本可以不互斥。读写锁(C++17起为std::shared_mutex)应运而生,它允许多个读线程同时持有锁,但写线程独占锁。
#include
#include
#include
std::shared_mutex rw_mtx; // 定义一个读写锁
int shared_data = 0;
void read_data() {
std::shared_lock lck(rw_mtx); // 读锁
std::cout << "Read data: " << shared_data << '\n';
}
void write_data(int value) {
std::unique_lock lck(rw_mtx); // 写锁
shared_data = value;
std::cout << "Write data: " << shared_data << '\n';
}
int main() {
std::thread readers[5];
std::thread writer(write_data, 42);
for (auto& th : readers) {
th = std::thread(read_data);
}
for (auto& th : readers) {
th.join();
}
writer.join();
return 0;
}
使用时要注意区分:读操作使用std::shared_lock,写操作使用std::unique_lock。这样,多个read_data线程可以并发执行,而write_data线程执行时,所有读写线程都必须等待。
最后来看看屏障。它的概念很简单:设置一个同步点,要求指定数量的线程都到达这个点后,大家才能一起继续往下执行。这在分阶段并行计算的场景中非常有用。
#include
#include
#include
#include
std::barrier sync_point(3); // 定义一个屏障,等待3个线程
void do_work(int id) {
std::cout << "Thread " << id << " is working\n";
sync_point.arrive_and_wait(); // 等待其他线程
std::cout << "Thread " << id << " has passed the barrier\n";
}
int main() {
std::vector threads;
for (int i = 0; i < 3; ++i) {
threads.emplace_back(do_work, i);
}
for (auto& th : threads) {
th.join();
}
return 0;
}
代码中,屏障sync_point被设置为需要等待3个线程。每个线程完成自己的“工作”阶段后,调用arrive_and_wait(),直到另外两个线程也到达此处,三个线程才会同时继续执行“通过屏障后”的代码。
以上就是C++中几种主流的线程同步机制。简单总结一下:互斥锁是基础,递归互斥锁解决特定嵌套问题,条件变量用于线程间协作,读写锁优化读多写少场景,屏障则用于阶段性的线程汇合。在实际开发中,根据具体的需求场景选择合适的同步工具,是构建健壮、高效多线程程序的关键一步。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
9