C++面试4-线程同步

分类 名称 C++标准 Windows API POSIX/Linux 功能描述
互斥锁 std::mutex CRITICAL_SECTION / CreateMutex pthread_mutex_t 保证同一时刻仅一个线程访问共享资源
自旋锁 std::atomic_flag - pthread_spinlock_t 忙等方式锁,适合短时间加锁
读写锁 std::shared_mutex (C++17) SRWLock pthread_rwlock_t 多读单写并行
条件变量 std::condition_variable ConditionVariable pthread_cond_t 等待条件满足再继续执行
信号量 std::counting_semaphore (C++20) CreateSemaphore sem_t 控制同时访问共享资源的线程数
原子操作 std::atomic<T> Interlocked* 系列 __sync_fetch_and_add 无锁同步,轻量高效
事件 - CreateEvent pthread_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();

}

相关推荐
罗湖老棍子几秒前
【例9.15】潜水员(信息学奥赛一本通- P1271)
c++·算法·动态规划·二维费用背包
兔子撩架构16 分钟前
Dubbo 的同步服务调用
java·后端·spring cloud
x***13391 小时前
MySQL 篇 - Java 连接 MySQL 数据库并实现数据交互
java·数据库·mysql
xuanzdhc1 小时前
Gitgit
java·linux·运维·服务器·c++·git
无心水1 小时前
【Python实战进阶】7、Python条件与循环实战详解:从基础语法到高级技巧
android·java·python·python列表推导式·python条件语句·python循环语句·python实战案例
一点★1 小时前
“equals”与“==”、“hashCode”的区别和使用场景
java·开发语言
N***H4861 小时前
SpringCloud实战十三:Gateway之 Spring Cloud Gateway 动态路由
java·spring cloud·gateway
程小k2 小时前
迷你编译器
c++·编辑器
s***w1122 小时前
SpringMVC新版本踩坑[已解决]
java
老李头喽2 小时前
走进单元测试
java·单元测试