
C 里“线程通信”这件事容易被讲成“mutex / condvar / atomic 三板斧”。但更准确的说法是线程通信 在共享地址空间内借助同步原语完成“数据传递”和“状态通知”。C11 之后标准库已经形成一套分层体系底层是原子与互斥中间是条件变量与信号量上层是 future / 任务模型再往上是消息队列、线程池、协程管道等设计模式。下面按“抽象层级 通信语义”系统梳理。一、共享内存所有通信的物理基础同一进程内的线程共享地址空间。因此“线程 A 写、线程 B 读”本身就是通信只是不安全。int g 0; // 线程 A: g 42; // 线程 B: printf(%d\n, g);没有同步时这是数据竞争属于未定义行为。所以 C 多线程通信不是“选一种魔法”而是共享内存负责传输同步原语负责排序与可见性。二、互斥与锁保护“共享状态”的通信1. 语义互斥不直接“传消息”而是保证临界区内对共享数据的修改对其他拿到锁的线程可见复合不变式不被并发破坏std::mutex m; int balance 0; void deposit(int x) { std::lock_guardstd::mutex lk(m); balance x; // 对其它加锁线程可见 }2. 家族原语语义std::mutex最基本互斥std::recursive_mutex同一线程可重入std::timed_mutex支持超时尝试std::shared_mutex(C17)多读单写std::lock_guard最简 RAII 锁std::unique_lock可延迟/可移动/配 condvarstd::shared_lock读锁std::scoped_lock多锁死锁安全3. 适用边界适合多字段对象一致性容器、状态机、配置、缓存业务不变式复杂难以用原子表达不适合高频计数器用 atomic单纯“等一件事发生”用 condvar / semaphore三、条件变量事件通知而不是数据传输1. 语义模型条件变量解决的是“某个条件成立时唤醒在等的线程。”它必须和 mutex 配合使用保护的是谓词状态不是数据本身。std::mutex m; std::condition_variable cv; std::queueint q; void producer() { { std::lock_guardstd::mutex lk(m); q.push(42); } cv.notify_one(); } void consumer() { std::unique_lockstd::mutex lk(m); cv.wait(lk, []{ return !q.empty(); }); int v q.front(); q.pop(); }2. 三个铁律wait必须有谓词否则丢唤醒 / 假唤醒都会出错notify前通常先修改受保护状态条件变量不携带数据只携带“状态可能变了”的信号3. 典型场景生产者 / 消费者任务队列状态机切换“数据就绪”通知四、原子变量轻量级状态与无锁通信1. 语义std::atomicT提供原子性读写内存序memory order跨线程可见性std::atomicbool ready{false}; int data 0; // 线程 A data 42; ready.store(true, std::memory_order_release); // 线程 B while (!ready.load(std::memory_order_acquire)) { // spin / yield } // 此处能看到 data 422. 能做什么停止标志atomicbool stop引用计数发布指针 / 数据缓冲区无锁栈、无锁队列、SPSC 通道3. C20 的原子等待std::atomicint state{0}; state.wait(0); // 阻塞直到值变化 state.store(1); state.notify_all();这比“mutex condvar”轻也比忙等高效。4. 边界原子适合单变量标志位计数器发布-订阅式指针不适合多字段事务复杂不变式“等 N 个条件同时成立”的复杂协调五、Future / Promise一次性结果通信1. 模型std::promiseint p; std::futureint f p.get_future(); std::thread t([p std::move(p)] mutable { p.set_value(compute()); }); int r f.get(); // 阻塞等结果或异常通信内容是一个返回值或一个异常且只能发生一次2. 上层封装工具作用std::promise手动写结果std::future读结果std::shared_future多消费者读同一结果std::packaged_task把“函数调用”绑到 futurestd::async一步完成“起任务 给 future”3. 适用后台计算返回结果线程启动结果 / 错误信息回传一次性请求-响应不适合持续消息流高频事件多生产者多消费者管道六、信号量C20配额与门控1. 语义std::counting_semaphore sem(3); // 最多 3 个线程进入 void worker() { sem.acquire(); do_work(); sem.release(); }acquire计数 0 则减一否则阻塞release计数加一唤醒等待者std::binary_semaphore是计数上限为 1 的特例2. 和 mutex 的区别维度mutexsemaphore所有者有谁锁谁解无任何人可 release用途保护临界区控制并发数 / 资源配额是否传数据否否3. 适用连接池线程数限流生产者消费者缓冲区槽位替代某些 condvar 计数器的手写模型七、Latch / BarrierC20阶段协调1.std::latch一次性倒计时std::latch lat(N); // N 个线程各自 do_work(); lat.count_down(); // 某线程 lat.wait(); // 等所有人完成适合任务拆分后等全部完成初始化阶段屏障2.std::barrier可重用阶段屏障std::barrier bar(N, []{ // 每轮结束时的汇合动作 }); while (running) { phase_work(); bar.arrive_and_wait(); }适合迭代算法并行 for 的分阶段同步仿真、渲染、数值计算八、消息传递把“共享”变成“传递”这是工业级并发里最重要的范式转变不要让大家抢同一块内存而是把数据所有权从一个线程移到另一个线程。1. 线程安全队列templateclass T class ThreadSafeQueue { std::queueT q; std::mutex m; std::condition_variable cv; public: void push(T v) { { std::lock_guardstd::mutex lk(m); q.push(std::move(v)); } cv.notify_one(); } T pop() { std::unique_lockstd::mutex lk(m); cv.wait(lk, [this]{ return !q.empty(); }); T v std::move(q.front()); q.pop(); return v; } };通信语义生产者拥有数据 → push → 所有权转移消费者 pop → 独占数据没有共享可变状态被并发访问2. 无锁队列 / SPSC 通道单生产者单消费者可用 ring buffer atomicMPMC 可用 CAS、epoch、hazard pointer游戏、交易系统、网络栈常用3. 为什么更高级死锁面更小可测试性更好线程边界清晰容易扩展到线程池 / actor 模型九、停止与取消C20 的jthread/stop_token线程通信不只是“传数据”还包括“让你停下”。void worker(std::stop_token st) { while (!st.stop_requested()) { do_periodic(); std::this_thread::sleep_for(10ms); } } std::jthread t(worker); // 析构时自动 request_stop() join()相关原语std::jthreadstd::stop_tokenstd::stop_sourcestd::stop_callback这是“控制面通信”和“数据面通信”同样重要。十、线程局部存储用“不共享”来通信thread_local int cache 0;这不是线程间传数据而是反方向通过避免共享消除同步成本。常见用法每线程缓存每线程随机数生成器每线程日志缓冲后续汇总reduce时才通信十一、高级 / 系统级通信1. 无锁结构lock-free stack / queueRCUhazard pointeratomic smart pointerstd::atomicstd::shared_ptrC20适合极低延迟、高争用、内核/交易/网络栈。2. 协程管道C20 起taskint producer(); taskvoid consumer() { int x co_await producer(); }协程本身不是线程但和线程模型结合后可以形成线程池 协程任务async / await 风格管道高并发 I/O 通信3. 共享内存 外部 IPC同一进程内是线程通信跨进程常借助mmap/shm管道 / socket消息队列但那些已经超出“C 标准线程库”范围。十二、选型地图你想做的事首选保护多字段状态mutex RAII 锁等“数据就绪”condition_variable简单标志 / 计数std::atomic一次性返回结果future/promise限制并发数counting_semaphore等所有任务完成latch每轮迭代汇合barrier生产者消费者线程安全队列低延迟无锁atomic CAS让线程优雅停jthreadstop_token避免共享thread_local/ 消息传递十三、核心结论C 多线程通信不是一堆孤立 API而是一张谱系最底层原子、内存序、mutex —— 解决可见性与互斥事件层condvar、semaphore、latch、barrier —— 解决“什么时候继续”任务层promise / future / packaged_task / async —— 解决“结果去哪”架构层消息队列、线程池、无锁结构、协程 —— 解决“系统怎么组织”新手学 mutex进阶学 condvar老手写消息传递架构师尽量少共享。