目录
[C++ 高并发编程实战:线程使用、同步机制与无锁队列详解](#C++ 高并发编程实战:线程使用、同步机制与无锁队列详解)
[1. 普通函数作为线程入口](#1. 普通函数作为线程入口)
[2. 带参数函数作为线程入口](#2. 带参数函数作为线程入口)
[3. 类成员函数作为线程入口](#3. 类成员函数作为线程入口)
[4. Lambda 表达式创建线程](#4. Lambda 表达式创建线程)
[1. 互斥锁 std::mutex](#1. 互斥锁 std::mutex)
[2. RAII 锁模板 std::lock_guard](#2. RAII 锁模板 std::lock_guard)
[3. 条件变量 std::condition_variable](#3. 条件变量 std::condition_variable)
[三、原子操作 std::atomic 与无锁编程基础](#三、原子操作 std::atomic 与无锁编程基础)
[1. 队列节点结构](#1. 队列节点结构)
[2. 无锁队列完整实现](#2. 无锁队列完整实现)
C++ 高并发编程实战:线程使用、同步机制与无锁队列详解
在高性能后端、游戏服务器、基础组件开发等场景中,C++ 多线程与无锁编程是提升程序并发能力、充分利用多核 CPU 的关键技术。本文将从线程基础创建方式入手,讲解常用线程同步方案,并深入到原子操作与无锁队列实现,由浅入后构建一套完整的高并发编程知识体系。
一、线程创建的四种常用方式
C++11 标准库提供了 std::thread,让线程创建变得简洁且统一,日常开发中主要有以下四种使用形式。
1. 普通函数作为线程入口
这是最基础的线程使用方式,直接将普通函数作为线程执行体。
cpp
void thread_func1() {
// 线程业务逻辑
}
int main() {
std::thread t1(thread_func1);
t1.join();
return 0;
}
2. 带参数函数作为线程入口
线程支持传入多个参数,编译器会自动完成参数转发,适配绝大多数带参业务逻辑。
cpp
void thread_func2(int a, std::string b) {
// 使用传入的参数
}
int main() {
std::thread t2(thread_func2, 10, "hello");
t2.join();
return 0;
}
3. 类成员函数作为线程入口
面向对象开发中,常需要将类成员函数运行在线程中,使用时必须传入对象地址。
cpp
class Test {
public:
void func() {}
};
int main() {
Test obj;
std::thread t3(&Test::func, &obj);
t3.join();
return 0;
}
4. Lambda 表达式创建线程
Lambda 写法轻便直观,适合简短、一次性的线程逻辑,是现代 C++ 高频用法。
cpp
int main() {
std::thread t4( {
// 直接编写线程执行代码
});
t4.join();
return 0;
}
二、线程同步与互斥核心机制
多线程并发访问共享资源时,必须通过同步机制避免数据竞争,常用方案如下。
1. 互斥锁 std::mutex
最基础的互斥手段,通过手动加锁、解锁保护临界区资源。

cpp
std::mutex mtx;
void func() {
mtx.lock();
// 临界区操作
mtx.unlock();
}
2. RAII 锁模板 std::lock_guard
利用栈对象生命周期自动管理锁,构造加锁、析构解锁,避免手动管理导致的死锁风险。

cpp
void func() {
std::lock_guard<std::mutex> lock(mtx);
// 临界区代码
}
具体说明:
- 类封装特性:
lock_guard是 C++11 <mutex>头文件)中的模板类,构造函数接收一个互斥锁(如std::mutex),自动调用锁的lock()方法获取锁;析构函数自动调用锁的unlock ()` 方法释放锁,全程无需手动操作。- 与智能指针的共性:两者都通过 "类的生命周期" 管理资源 ------ 智能指针封装堆内存(构造时分配 / 接收内存,析构时自动释放),
lock_guard封装互斥锁(构造时加锁,析构时解锁),均避免了手动管理资源(释放内存、解锁)导致的泄漏或死锁。- 细微区别:
lock_guard不可拷贝、不可移动,生命周期严格绑定到当前作用域(离开作用域自动析构解锁);而智能指针(如unique_ptr)支持移动,shared_ptr支持共享所有权,功能更灵活,但核心的 RAII 封装思想完全一致。lock_guard` 和 `unique_lock` 均是 C++11 标准库(`<mutex>` 头文件)中,用于**封装互斥锁、实现 RAII 机制**的模板类,二者核心目的一致,且存在明确的继承/设计关联,具体联系如下:
与unique_lock的联系:
核心联系(本质共性)
核心设计思想一致 :两者均基于 RAII(资源获取即初始化) 机制,通过类的生命周期管理互斥锁------构造函数自动调用 `mutex::lock()` 获取锁,析构函数自动调用 `mutex::unlock()` 释放锁,彻底避免手动加锁/解锁导致的死锁(忘记解锁)或资源泄漏。
核心功能一致:核心作用都是"安全管理互斥锁",保护多线程中的临界区,避免数据竞争,简化多线程同步代码的编写。
底层依赖一致:两者都必须依赖 `std::mutex`(或其他可锁类型,如 `std::recursive_mutex`)才能工作,自身不具备"锁"的功能,仅负责对锁的生命周期进行封装管理。
不可拷贝特性一致 :两者均被设计为 不可拷贝(拷贝构造函数和拷贝赋值运算符被删除),避免多个锁管理对象同时管理同一个互斥锁,导致重复解锁或解锁异常。
关键补充
`unique_lock` 是 `lock_guard` 的 增强版,在继承 RAII 核心思想的基础上,扩展了更灵活的功能,可以理解为: `unique_lock = lock_guard + 灵活解锁/延迟加锁/条件变量配合`
正因为二者核心设计一致,`unique_lock` 才能兼容 `lock_guard` 的所有基础用法(如简单的临界区保护),同时解决 `lock_guard` 的局限性。
注意事项
两者均不可拷贝,但 `unique_lock` 支持移动语义(可通过 `std::move` 转移所有权),`lock_guard` 不支持移动,生命周期严格绑定到当前作用域。
`lock_guard` 仅支持"构造时加锁、析构时解锁",无法手动解锁、无法延迟加锁;`unique_lock` 支持 `unlock()` 手动解锁、构造时延迟加锁(传入 `std::defer_lock`),适配条件变量(`condition_variable::wait()`)等复杂场景。
性能上,`lock_guard` 更轻量(无额外状态维护),`unique_lock` 因支持灵活操作,有轻微性能开销,简单场景优先用 `lock_guard`,复杂场景用 `unique_lock`。
3. 条件变量 std::condition_variable
用于线程间等待与唤醒,常配合 std::unique_lock 实现生产者 - 消费者模型,支持等待时自动释放锁。

cpp
std::condition_variable cv;
void consumer() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, {
// 满足条件才继续执行
return true;
});
}
cpp
int main()
{
int x = 1;
condition_variable cv;
mutex mtx;
bool flag = false;
//线程1打印奇数
thread t1([&]() {
for (int i = 1;i <= 100;i++)
{
unique_lock<mutex>lock(mtx);
//确保线程1先启动
if (flag != false)
cv.wait(lock);
cout <<"线程1:" << x++ << endl;
flag = true;
//唤醒线程2
cv.notify_one();
}
});
//线程2打印偶数
thread t2([&]() {
for (int i = 1;i <= 100;i++)
{
unique_lock<mutex>lock(mtx);
if (flag != true)
cv.wait(lock);
cout << "线程2:" << x++ << endl;
flag = false;
//唤醒线程1
cv.notify_one();
}
});
//线程等待释放
t1.join();
t2.join();
return 0;
}
三、原子操作 std::atomic 与无锁编程基础
在高并发场景下,锁的开销可能成为性能瓶颈,std::atomic 提供了原子操作,可在不加锁的情况下保证变量访问的线程安全性,是无锁编程的基石。
原子类型支持 load() 读取、store() 赋值、++/-- 等原子运算,而核心的 CAS(比较并交换)操作 compare_exchange_strong,更是实现无锁数据结构的关键。其原理为:先比较内存值与预期值是否相等,相等则更新为目标值,否则不做修改,通过循环重试保证操作最终成功。

cpp
#define _CRT_SECURE_NO_WARNINGS
#include<thread>
#include<iostream>
#include <mutex>
#include<atomic>
using namespace std;
int main()
{
atomic<size_t> x = 0;
int n = 100000;
//线程1打印奇数
thread t1([&]() {
for (int i = 1;i <= n;i++)
{
x++;
}
});
//线程2打印偶数
thread t2([&]() {
for (int i = 1;i <= n;i++)
{
x++;
}
});
//线程等待释放
t1.join();
t2.join();
cout << x << endl;
return 0;
}
四、高性能无锁队列实现
无锁队列基于原子指针与 CAS 实现,避免锁竞争带来的阻塞,在高并发读写场景下性能优势显著。
1. 队列节点结构
节点内部使用原子指针指向下一节点,保证多线程下指针修改安全。
cpp
template <typename T>
struct Node {
T data;
std::atomic<Node*> next;
Node(T val) : data(val), next(nullptr) {}
};
2. 无锁队列完整实现
通过原子头尾指针,结合 CAS 循环实现无锁入队、出队操作,并使用虚拟头节点简化边界处理。
cpp
template <typename T>
class LockFreeQueue {
private:
std::atomic<Node<T>*> head;
std::atomic<Node<T>*> tail;
public:
LockFreeQueue() {
Node<T>* dummy = new Node<T>(T());
head = dummy;
tail = dummy;
}
// 无锁入队
void enqueue(const T& data) {
Node<T>* new_node = new Node<T>(data);
Node<T>* old_tail = nullptr;
while (true) {
old_tail = tail.load();
Node<T>* next = old_tail->next.load();
if (tail == old_tail) {
if (next == nullptr) {
if (old_tail->next.compare_exchange_weak(next, new_node)) {
tail.compare_exchange_strong(old_tail, new_node);
return;
}
}
}
}
}
// 无锁出队
bool dequeue(T& data) {
Node<T>* old_head = nullptr;
while (true) {
old_head = head.load();
Node<T>* tail_ptr = tail.load();
Node<T>* next = old_head->next.load();
if (old_head == tail_ptr) {
if (next == nullptr) return false;
tail.compare_exchange_strong(tail_ptr, next);
continue;
}
data = next->data;
if (head.compare_exchange_strong(old_head, next)) {
delete old_head;
return true;
}
}
}
};
五、总结
本文从基础线程创建,到锁机制同步,再到原子操作与无锁队列,覆盖了 C++ 高并发编程的核心知识点。std::thread 提供了灵活的线程使用方式,互斥锁与条件变量满足常规同步需求,而基于 std::atomic 的无锁队列,则为高性能场景提供了更优的解决方案。
在实际开发中,简单场景优先使用锁保证安全,高并发瓶颈场景可采用无锁结构优化,合理搭配不同方案,才能写出高效、稳定的并发程序。