C++面试4-线程同步

分类名称C++标准Windows APIPOSIX/Linux功能描述
互斥锁std::mutexCRITICAL_SECTION / CreateMutexpthread_mutex_t保证同一时刻仅一个线程访问共享资源
自旋锁std::atomic_flag-pthread_spinlock_t忙等方式锁,适合短时间加锁
读写锁std::shared_mutex (C++17)SRWLockpthread_rwlock_t多读单写并行
条件变量std::condition_variableConditionVariablepthread_cond_t等待条件满足再继续执行
信号量std::counting_semaphore (C++20)CreateSemaphoresem_t控制同时访问共享资源的线程数
原子操作std::atomic<T>Interlocked* 系列__sync_fetch_and_add无锁同步,轻量高效
事件-CreateEventpthread_cond_signal 模拟用于线程间通知或唤醒
屏障std::barrier (C++20)-pthread_barrier_t等所有线程到达同一点后一起继续

二、各类同步机制示例

1️⃣ 互斥锁(mutex)

✅ C++ 标准版

#include <iostream>
#include <thread>
#include <mutex>

int counter = 0;
std::mutex mtx;

void add() {
    for (int i = 0; i < 100000; ++i) {
        std::lock_guard<std::mutex> lock(mtx); // 自动加解锁
        ++counter;
    }
}

int main() {
    std::thread t1(add), t2(add);
    t1.join(); t2.join();
    std::cout << "counter = " << counter << std::endl;
}
counter = 200000

#include <windows.h>
#include <iostream>

Window 版本:

CRITICAL_SECTION cs;
int counter = 0;

DWORD WINAPI add(LPVOID) {
    for (int i = 0; i < 100000; ++i) {
        EnterCriticalSection(&cs);
        ++counter;
        LeaveCriticalSection(&cs);
    }
    return 0;
}

int main() {
    InitializeCriticalSection(&cs);
    HANDLE t1 = CreateThread(nullptr, 0, add, nullptr, 0, nullptr);
    HANDLE t2 = CreateThread(nullptr, 0, add, nullptr, 0, nullptr);
    WaitForMultipleObjects(2, new HANDLE[2]{t1, t2}, TRUE, INFINITE);
    std::cout << "counter = " << counter << std::endl;
    DeleteCriticalSection(&cs);
}


2 条件变量(condition_variable)

#include <condition_variable>
#include <mutex>
#include <thread>
#include <queue>
#include <iostream>

std::mutex mtx;
std::condition_variable cv;
std::queue<int> q;
bool done = false;

void producer() {
    for (int i = 0; i < 5; ++i) {
        {
            std::lock_guard<std::mutex> lock(mtx);
            q.push(i);
        }
        cv.notify_one();
    }
    done = true;
    cv.notify_all();
}

void consumer() {
    while (true) {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, [] { return !q.empty() || done; });
        if (!q.empty()) {
            std::cout << "Consume: " << q.front() << "\n";
            q.pop();
        } else if (done) break;
    }
}

int main() {
    std::thread t1(producer), t2(consumer);
    t1.join(); t2.join();
}


打印:

Consume: 0
Consume: 1
Consume: 2
Consume: 3
Consume: 4

3  信号量(semaphore)

#include <iostream>
#include <thread>
#include <semaphore>

std::counting_semaphore<2> sem(2); // 最多允许2线程同时访问

void worker(int id) {
    sem.acquire();
    std::cout << "Thread " << id << " working\n";
    std::this_thread::sleep_for(std::chrono::milliseconds(500));
    sem.release();
}

int main() {
    std::thread t1(worker, 1), t2(worker, 2), t3(worker, 3);
    t1.join(); t2.join(); t3.join();
}
4️⃣ 原子变量(atomic)

#include <atomic>
#include <thread>
#include <iostream>

std::atomic<int> counter = 0;

void add() {
    for (int i = 0; i < 100000; ++i)
        counter.fetch_add(1);
}

int main() {
    std::thread t1(add), t2(add);
    t1.join(); t2.join();
    std::cout << "counter = " << counter << std::endl;
}

5️⃣ 读写锁(shared_mutex)

#include <shared_mutex>
#include <thread>
#include <iostream>

std::shared_mutex rwlock;
int data = 0;

void reader(int id) {
    std::shared_lock<std::shared_mutex> lock(rwlock);
    std::cout << "Reader " << id << " reads " << data << "\n";
}

void writer(int id) {
    std::unique_lock<std::shared_mutex> lock(rwlock);
    ++data;
    std::cout << "Writer " << id << " updates to " << data << "\n";
}

int main() {
    std::thread t1(writer, 1);
    std::thread t2(reader, 2);
    std::thread t3(reader, 3);
    t1.join(); t2.join(); t3.join();
}

三、选择建议总结

场景推荐同步方式原因
数据访问互斥std::mutex / pthread_mutex_t经典安全
短时间锁竞争自旋锁 / 原子操作避免上下文切换
生产者-消费者条件变量 + 队列高效等待唤醒
多读少写数据读写锁读多线程并行
控制资源数量信号量可同时运行 N 个线程
多线程阶段同步屏障 std::barrier阶段性同步
事件触发/通知条件变量 / Windows Event明确触发唤醒机制

Writer 1 updates to 1
Reader 2 reads 1
Reader 3 reads 1

这是理解多线程同步机制的重要一环。我们来一步步详细拆解:

一、首先复习一下:锁的目的

多线程编程中,多个线程同时访问共享资源(变量、文件、内存等)时,会产生数据竞争(Data Race)不一致性(Inconsistency)

为防止这种情况,我们用各种同步原语(Synchronization Primitives)控制访问顺序

  • std::mutex:互斥锁

  • std::recursive_mutex:可重入互斥锁

  • std::timed_mutex:带超时的互斥锁

  • std::shared_mutex(C++17):读写锁(shared-exclusive lock)

  • 二、读写锁(shared_mutex)是什么?

    读写锁是一种特殊的互斥机制,它区分了两种访问:

类型权限是否互斥适用场景
读锁(shared lock)只读访问多个线程可以同时加读锁多线程“读多写少”
写锁(unique lock)修改访问只能有一个线程加写锁,且此时不能有任何读锁修改共享数据时

🧩 三、为什么要用读写锁,而不是普通互斥锁?

✅ 1. 提升并发性能

普通的 mutex完全互斥的:

  • 线程 A 正在读取数据;

  • 线程 B 想读,也得等;

  • 明明两个线程都只读,没有写操作,其实不会互相影响,但 mutex 仍强制串行化。

这会大大降低性能。

读写锁 允许:

多个线程同时读取,只在写入时互斥。

这对“读多写少”的场景(例如缓存查询、配置读取)性能提升明显。

✅ 2. 避免不必要的等待

std::shared_mutex rwLock;
int value = 0;

void reader(int id) {
    std::shared_lock lock(rwLock); // 读锁
    std::cout << "Reader " << id << " sees value = " << value << "\n";
}

void writer(int id) {
    std::unique_lock lock(rwLock); // 写锁
    ++value;
    std::cout << "Writer " << id << " modifies value = " << value << "\n";
}

如果我们用普通 std::mutex

  • 即使 10 个线程都只是读取 value,也得一个个排队;

  • shared_mutex,这 10 个线程可以同时读。

✅ 3. 资源访问模式不同

互斥锁(mutex)保护的是「谁都不能同时访问」;
读写锁(rwlock)保护的是「谁不能同时写」。

场景用互斥锁用读写锁
只有一个房门,所有人都排队进✅ 合理但慢⚠️ 太保守
图书馆:很多人能同时看书,只有管理员能改书❌ 阻塞所有人✅ 高效

#include <iostream>
#include <thread>
#include <shared_mutex>
#include <vector>
#include <chrono>

std::shared_mutex rwLock;
int data = 0;

void reader(int id) {
    for (int i = 0; i < 3; ++i) {
        std::shared_lock<std::shared_mutex> lock(rwLock); // 读锁
        std::cout << "Reader " << id << " reads data = " << data << "\n";
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}

void writer(int id) {
    for (int i = 0; i < 3; ++i) {
        std::unique_lock<std::shared_mutex> lock(rwLock); // 写锁
        ++data;
        std::cout << "Writer " << id << " writes data = " << data << "\n";
        std::this_thread::sleep_for(std::chrono::milliseconds(200));
    }
}

int main() {
    std::vector<std::thread> threads;
    // 1个写者,3个读者
    threads.emplace_back(writer, 1);
    for (int i = 0; i < 3; ++i)
        threads.emplace_back(reader, i+1);

    for (auto &t : threads)
        t.join();
}
 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值