1. 引言
在现代计算机系统中,多核处理器已成为标配。为了充分利用硬件资源,提高程序性能,多线程编程成为C++开发者必须掌握的核心技能之一。C++11标准引入了原生的多线程支持,使得编写跨平台的多线程程序变得更加简单和安全。
本文将系统性地介绍C++多线程编程的基础知识,涵盖线程创建与管理、互斥量与锁、条件变量、原子变量等核心概念。
2. 线程(Thread)
2.1 线程的基本概念
线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。一个进程可以包含多个线程,这些线程共享进程的资源(如内存空间、文件描述符等),但拥有独立的执行栈和程序计数器。
2.2 C++中的线程创建
C++11通过<thread>头文件提供了线程支持。创建线程的基本方式如下:
cpp
#include <iostream>
#include <thread>
void thread_function() {
std::cout << "Hello from thread!" << std::endl;
}
int main() {
// 创建线程并立即执行
std::thread t(thread_function);
// 等待线程执行完成
t.join();
std::cout << "Main thread finished" << std::endl;
return 0;
}
2.3 线程的几种创建方式
cpp
#include <iostream>
#include <thread>
#include <functional>
// 方式1:函数指针
void func1() {
std::cout << "Function pointer thread" << std::endl;
}
// 方式2:Lambda表达式
auto lambda_func = []() {
std::cout << "Lambda thread" << std::endl;
};
// 方式3:函数对象
class Functor {
public:
void operator()() {
std::cout << "Functor thread" << std::endl;
}
};
// 方式4:成员函数
class MyClass {
public:
void member_func() {
std::cout << "Member function thread" << std::endl;
}
};
int main() {
// 1. 函数指针
std::thread t1(func1);
// 2. Lambda表达式
std::thread t2(lambda_func);
// 3. 函数对象
std::thread t3(Functor());
// 4. 成员函数
MyClass obj;
std::thread t4(&MyClass::member_func, &obj);
t1.join();
t2.join();
t3.join();
t4.join();
return 0;
}
2.4 线程的分离与等待
cpp
#include <iostream>
#include <thread>
#include <chrono>
void worker_thread() {
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "Worker thread finished" << std::endl;
}
int main() {
std::thread t(worker_thread);
// 分离线程(主线程不等待子线程)
// t.detach();
// 等待线程完成(推荐方式)
t.join();
// 检查线程是否可join
if (t.joinable()) {
t.join();
}
return 0;
}
3. 互斥量(Mutex)与锁(Lock)
3.1 数据竞争问题
当多个线程同时访问共享数据时,如果没有适当的同步机制,就会发生数据竞争(Data Race),导致未定义行为。
cpp
#include <iostream>
#include <thread>
#include <vector>
int counter = 0; // 共享数据
void increment() {
for (int i = 0; i < 100000; ++i) {
++counter; // 这里会发生数据竞争
}
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
// 结果可能不是200000
std::cout << "Counter: " << counter << std::endl;
return 0;
}
3.2 互斥量的基本使用
cpp
#include <iostream>
#include <thread>
#include <mutex>
#include <vector>
int counter = 0;
std::mutex mtx; // 互斥量
void safe_increment() {
for (int i = 0; i < 100000; ++i) {
mtx.lock(); // 加锁
++counter; // 临界区
mtx.unlock(); // 解锁
}
}
int main() {
std::thread t1(safe_increment);
std::thread t2(safe_increment);
t1.join();
t2.join();
// 结果一定是200000
std::cout << "Counter: " << counter << std::endl;
return 0;
}
3.3 不同类型的互斥量
C++提供了多种互斥量类型,适用于不同的场景:
cpp
#include <mutex>
#include <shared_mutex>
#include <thread>
// 1. std::mutex - 基本互斥量
std::mutex basic_mutex;
// 2. std::recursive_mutex - 可重入互斥量
std::recursive_mutex recursive_mutex;
// 3. std::timed_mutex - 带超时的互斥量
std::timed_mutex timed_mutex;
// 4. std::recursive_timed_mutex - 可重入带超时的互斥量
std::recursive_timed_mutex recursive_timed_mutex;
// 5. std::shared_mutex - 读写锁(C++17)
std::shared_mutex shared_mutex;
3.4 锁管理类(RAII风格)
手动管理锁的获取和释放容易出错,C++提供了几种锁管理类:
cpp
#include <iostream>
#include <thread>
#include <mutex>
#include <vector>
int counter = 0;
std::mutex mtx;
void increment_with_lock_guard() {
for (int i = 0; i < 100000; ++i) {
// std::lock_guard - 最简单的RAII锁
std::lock_guard<std::mutex> lock(mtx);
++counter;
}
}
void increment_with_unique_lock() {
for (int i = 0; i < 100000; ++i) {
// std::unique_lock - 更灵活的RAII锁
std::unique_lock<std::mutex> lock(mtx);
++counter;
// 可以手动解锁
lock.unlock();
// 做一些不需要锁的操作
// 可以重新加锁
lock.lock();
// 继续操作共享数据
}
}
void increment_with_scoped_lock() {
for (int i = 0; i < 100000; ++i) {
// std::scoped_lock - C++17引入,可以锁多个互斥量
std::scoped_lock lock(mtx);
++counter;
}
}
int main() {
std::thread t1(increment_with_lock_guard);
std::thread t2(increment_with_unique_lock);
std::thread t3(increment_with_scoped_lock);
t1.join();
t2.join();
t3.join();
std::cout << "Counter: " << counter << std::endl;
return 0;
}
3.5 死锁与避免
cpp
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx1, mtx2;
void thread1() {
// 错误的顺序 - 可能导致死锁
mtx1.lock();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
mtx2.lock();
std::cout << "Thread 1 acquired both locks" << std::endl;
mtx2.unlock();
mtx1.unlock();
}
void thread2() {
// 错误的顺序 - 可能导致死锁
mtx2.lock();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
mtx1.lock();
std::cout << "Thread 2 acquired both locks" << std::endl;
mtx1.unlock();
mtx2.unlock();
}
void safe_thread1() {
// 使用std::lock同时锁定多个互斥量,避免死锁
std::lock(mtx1, mtx2);
std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock);
std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);
std::cout << "Safe Thread 1 acquired both locks" << std::endl;
}
void safe_thread2() {
// 使用std::scoped_lock(C++17)
std::scoped_lock lock(mtx1, mtx2);
std::cout << "Safe Thread 2 acquired both locks" << std::endl;
}
int main() {
// 可能导致死锁的示例
// std::thread t1(thread1);
// std::thread t2(thread2);
// 安全的示例
std::thread t1(safe_thread1);
std::thread t2(safe_thread2);
t1.join();
t2.join();
return 0;
}
4. 条件变量(Condition Variable)
4.1 条件变量的基本概念
条件变量用于线程间的同步,允许一个或多个线程等待某个条件成立。
cpp
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
std::mutex mtx;
std::condition_variable cv;
std::queue<int> data_queue;
bool finished = false;
void producer() {
for (int i = 0; i < 10; ++i) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
{
std::lock_guard<std::mutex> lock(mtx);
data_queue.push(i);
std::cout << "Produced: " << i << std::endl;
}
cv.notify_one(); // 通知一个等待的消费者
}
{
std::lock_guard<std::mutex> lock(mtx);
finished = true;
}
cv.notify_all(); // 通知所有等待的消费者
}
void consumer() {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
// 等待条件:队列不为空或生产完成
cv.wait(lock, []() {
return !data_queue.empty() || finished;
});
// 检查是否应该退出
if (finished && data_queue.empty()) {
break;
}
// 消费数据
int data = data_queue.front();
data_queue.pop();
lock.unlock();
std::cout << "Consumed: " << data << std::endl;
// 模拟处理时间
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
}
int main() {
std::thread prod(producer);
std::thread cons1(consumer);
std::thread cons2(consumer);
prod.join();
cons1.join();
cons2.join();
std::cout << "All threads finished" << std::endl;
return 0;
}
4.2 条件变量的使用模式
cpp
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
class SimpleSemaphore {
private:
std::mutex mtx;
std::condition_variable cv;
int count;
public:
SimpleSemaphore(int initial = 0) : count(initial) {}
void acquire() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [this]() { return count > 0; });
--count;
}
void release() {
std::unique_lock<std::mutex> lock(mtx);
++count;
cv.notify_one();
}
};
void worker(int id, SimpleSemaphore& sem) {
sem.acquire();
std::cout << "Worker " << id << " started" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Worker " << id << " finished" << std::endl;
sem.release();
}
int main() {
SimpleSemaphore sem(2); // 允许同时运行2个worker
std::thread workers[5];
for (int i = 0; i < 5; ++i) {
workers[i] = std::thread(worker, i, std::ref(sem));
}
for (auto& w : workers) {
w.join();
}
return 0;
}
5. 原子变量(Atomic)
5.1 原子操作的基本概念
原子操作是不可分割的操作,要么完全执行,要么完全不执行,不会出现中间状态。
cpp
#include <iostream>
#include <thread>
#include <atomic>
#include <vector>
// 使用原子变量
std::atomic<int> atomic_counter(0);
// 普通变量(用于对比)
int non_atomic_counter = 0;
void atomic_increment() {
for (int i = 0; i < 100000; ++i) {
++atomic_counter; // 原子操作
}
}
void non_atomic_increment() {
for (int i = 0; i < 100000; ++i) {
++non_atomic_counter; // 非原子操作,可能发生数据竞争
}
}
int main() {
std::thread t1(atomic_increment);
std::thread t2(atomic_increment);
std::thread t3(non_atomic_increment);
std::thread t4(non_atomic_increment);
t1.join();
t2.join();
t3.join();
t4.join();
std::cout << "Atomic counter: " << atomic_counter << std::endl;
std::cout << "Non-atomic counter: " << non_atomic_counter << std::endl;
return 0;
}
5.2 原子变量的内存顺序
cpp
#include <iostream>
#include <thread>
#include <atomic>
std::atomic<int> x(0), y(0);
int r1, r2;
void thread1() {
x.store(1, std::memory_order_relaxed);
r1 = y.load(std::memory_order_relaxed);
}
void thread2() {
y.store(1, std::memory_order_relaxed);
r2 = x.load(std::memory_order_relaxed);
}
void test_memory_order() {
int count = 0;
for (int i = 0; i < 10000; ++i) {
x = 0;
y = 0;
r1 = 0;
r2 = 0;
std::thread t1(thread1);
std::thread t2(thread2);
t1.join();
t2.join();
if (r1 == 0 && r2 == 0) {
++count;
}
}
std::cout << "Relaxed ordering: r1 == 0 && r2 == 0 happened "
<< count << " times" << std::endl;
}
int main() {
test_memory_order();
return 0;
}
5.3 原子变量的高级操作
cpp
#include <iostream>
#include <thread>
#include <atomic>
#include <vector>
std::atomic<int> counter(0);
void increment_with_fetch_add() {
for (int i = 0; i < 100000; ++i) {
// fetch_add返回旧值,然后加上给定值
int old_value = counter.fetch_add(1);
// 可以基于旧值做一些操作
}
}
void compare_exchange_example() {
int expected = 0;
while (!counter.compare_exchange_weak(expected, expected + 1)) {
// 如果counter != expected,expected会被更新为counter的当前值
// 然后重试
}
}
void atomic_operations() {
std::atomic<int> value(10);
// 各种原子操作
value.store(20); // 存储
int loaded = value.load(); // 加载
value.fetch_add(5); // 原子加
value.fetch_sub(3); // 原子减
value.fetch_and(0xFF); // 原子与
value.fetch_or(0x0F); // 原子或
value.fetch_xor(0xAA); // 原子异或
// 交换
int old = value.exchange(100);
std::cout << "Final value: " << value.load() << std::endl;
}
int main() {
std::thread t1(increment_with_fetch_add);
std::thread t2(increment_with_fetch_add);
t1.join();
t2.join();
std::cout << "Counter: " << counter << std::endl;
atomic_operations();
return 0;
}