C++多线程全面详解
1. 线程基础概念
原理
线程是操作系统能够进行运算调度的最小单位,被包含在进程之中,是进程中的实际运作单位。一个进程可以有多个线程,这些线程共享进程的资源(如内存空间、文件句柄等),但每个线程有自己的栈空间和寄存器状态。
代码示例:创建线程
cpp
#include <iostream>
#include <thread>
#include <chrono>
// 函数形式的线程任务
void printNumbers(int n) {
for (int i = 1; i <= n; ++i) {
std::cout << "Thread 1: " << i << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
// Lambda表达式作为线程任务
auto lambdaTask = []() {
for (int i = 10; i > 0; --i) {
std::cout << "Thread 2: " << i << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(150));
}
};
int main() {
std::cout << "主线程开始" << std::endl;
// 创建线程,传入函数和参数
std::thread t1(printNumbers, 5);
// 创建线程,传入lambda表达式
std::thread t2(lambdaTask);
// 主线程等待子线程完成
t1.join();
t2.join();
std::cout << "所有线程执行完毕" << std::endl;
return 0;
}
2. 线程管理
2.1 join与detach原理
- join(): 阻塞当前线程,直到目标线程执行完毕
- detach(): 分离线程,使线程在后台运行,主线程无需等待
- joinable(): 检查线程是否可以被join或detach
代码示例:线程管理
cpp
#include <iostream>
#include <thread>
#include <chrono>
void task(int id, int duration) {
std::this_thread::sleep_for(std::chrono::milliseconds(duration));
std::cout << "线程" << id << "完成,耗时" << duration << "ms" << std::endl;
}
int main() {
std::thread t1(task, 1, 1000);
std::thread t2(task, 2, 500);
std::thread t3(task, 3, 800);
// 检查线程状态
std::cout << "t1可连接: " << (t1.joinable() ? "是" : "否") << std::endl;
std::cout << "t2可连接: " << (t2.joinable() ? "是" : "否") << std::endl;
// 分离线程t3,让它独立运行
t3.detach();
std::cout << "t3已分离" << std::endl;
// 等待t1和t2完成
t1.join();
t2.join();
// 分离后不能再join
// t3.join(); // 错误!已分离的线程不能join
std::cout << "主线程结束" << std::endl;
// 给分离的线程一些时间完成
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
return 0;
}
2.2 线程ID和硬件并发数
cpp
#include <iostream>
#include <thread>
#include <vector>
void printThreadInfo(int id) {
std::cout << "线程" << id << " ID: " << std::this_thread::get_id() << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
int main() {
// 获取硬件支持的并发线程数
unsigned int n = std::thread::hardware_concurrency();
std::cout << "硬件支持的最大并发线程数: " << n << std::endl;
// 主线程ID
std::cout << "主线程ID: " << std::this_thread::get_id() << std::endl;
// 创建多个线程
std::vector<std::thread> threads;
for (int i = 0; i < 4; ++i) {
threads.emplace_back(printThreadInfo, i);
}
// 等待所有线程完成
for (auto& t : threads) {
t.join();
}
return 0;
}
3. 数据竞争与互斥锁
3.1 数据竞争原理
当多个线程同时访问共享数据,且至少有一个线程在修改数据时,如果没有适当的同步机制,就会发生数据竞争,导致未定义行为。
3.2 std::mutex原理
互斥锁(mutex)是最基本的同步原语,用于保护共享数据,确保同一时间只有一个线程可以访问临界区。
代码示例:数据竞争与互斥锁
cpp
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
// 共享数据
int sharedCounter = 0;
// 互斥锁
std::mutex mtx;
void incrementWithoutMutex(int iterations) {
for (int i = 0; i < iterations; ++i) {
++sharedCounter;
}
}
void incrementWithMutex(int iterations) {
for (int i = 0; i < iterations; ++i) {
std::lock_guard<std::mutex> lock(mtx);
++sharedCounter;
}
}
void incrementWithManualMutex(int iterations) {
for (int i = 0; i < iterations; ++i) {
mtx.lock(); // 手动加锁
++sharedCounter;
mtx.unlock(); // 手动解锁
}
}
int main() {
const int iterations = 100000;
// 测试无锁情况(数据竞争)
sharedCounter = 0;
std::thread t1(incrementWithoutMutex, iterations);
std::thread t2(incrementWithoutMutex, iterations);
t1.join();
t2.join();
std::cout << "无锁结果 (可能错误): " << sharedCounter
<< " (期望: " << 2*iterations << ")" << std::endl;
// 测试使用lock_guard
sharedCounter = 0;
std::thread t3(incrementWithMutex, iterations);
std::thread t4(incrementWithMutex, iterations);
t3.join();
t4.join();
std::cout << "使用lock_guard结果: " << sharedCounter << std::endl;
// 测试手动锁管理
sharedCounter = 0;
std::thread t5(incrementWithManualMutex, iterations);
std::thread t6(incrementWithManualMutex, iterations);
t5.join();
t6.join();
std::cout << "手动锁管理结果: " << sharedCounter << std::endl;
return 0;
}
4. 死锁与避免
原理
死锁发生在两个或多个线程相互等待对方释放锁时。常见解决方案:
- 总是以相同顺序获取锁
- 使用std::lock同时锁定多个互斥量
- 使用std::scoped_lock(C++17)
代码示例:死锁与避免
cpp
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx1, mtx2;
// 可能导致死锁的版本
void thread1_unsafe() {
std::lock_guard<std::mutex> lock1(mtx1);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::lock_guard<std::mutex> lock2(mtx2);
std::cout << "线程1获得两个锁" << std::endl;
}
void thread2_unsafe() {
std::lock_guard<std::mutex> lock2(mtx2);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::lock_guard<std::mutex> lock1(mtx1);
std::cout << "线程2获得两个锁" << std::endl;
}
// 安全版本1:固定加锁顺序
void thread1_safe_order() {
std::lock_guard<std::mutex> lock1(mtx1);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::lock_guard<std::mutex> lock2(mtx2);
std::cout << "线程1(安全顺序)获得两个锁" << std::endl;
}
void thread2_safe_order() {
std::lock_guard<std::mutex> lock1(mtx1); // 与thread1相同顺序
std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::lock_guard<std::mutex> lock2(mtx2);
std::cout << "线程2(安全顺序)获得两个锁" << std::endl;
}
// 安全版本2:使用std::lock同时锁定
void thread1_safe_lock() {
std::unique_lock<std::mutex> lock1(mtx1, std::defer_lock);
std::unique_lock<std::mutex> lock2(mtx2, std::defer_lock);
std::lock(lock1, lock2); // 同时锁定,避免死锁
std::cout << "线程1(std::lock)获得两个锁" << std::endl;
}
void thread2_safe_lock() {
std::unique_lock<std::mutex> lock1(mtx1, std::defer_lock);
std::unique_lock<std::mutex> lock2(mtx2, std::defer_lock);
std::lock(lock1, lock2);
std::cout << "线程2(std::lock)获得两个锁" << std::endl;
}
int main() {
std::cout << "=== 死锁示例 ===" << std::endl;
// 注释掉下面的代码,因为可能导致死锁
// std::thread t1(thread1_unsafe);
// std::thread t2(thread2_unsafe);
// t1.join();
// t2.join();
std::cout << "\n=== 安全版本1:固定顺序 ===" << std::endl;
std::thread t3(thread1_safe_order);
std::thread t4(thread2_safe_order);
t3.join();
t4.join();
std::cout << "\n=== 安全版本2:std::lock ===" << std::endl;
std::thread t5(thread1_safe_lock);
std::thread t6(thread2_safe_lock);
t5.join();
t6.join();
return 0;
}
死锁详细的时间线分析
时间线(假设两个线程几乎同时启动):
t=0ms:
- 线程t1: 锁定mtx1(成功)
- 线程t2: 锁定mtx2(成功)
t=0-10ms:
- 线程t1: 持有mtx1,休眠10ms
- 线程t2: 持有mtx2,休眠10ms
t=10ms:
- 线程t1: 醒来,尝试锁定mtx2 → 发现mtx2已被t2锁定 → 进入等待状态
- 线程t2: 醒来,尝试锁定mtx1 → 发现mtx1已被t1锁定 → 进入等待状态
结果:
- t1持有mtx1,等待mtx2
- t2持有mtx2,等待mtx1
- 形成循环等待,永久阻塞 → 死锁!
可视化死锁图
线程1 (t1) 线程2 (t2)
│ │
▼ ▼
持有 mtx1 持有 mtx2
│ │
│ 等待 mtx2 ←───── │ 等待 mtx1
│ │
▼ ▼
阻塞 阻塞
║ ║
╚════════循环等待═══════╝
5. 条件变量
原理
条件变量用于线程间的同步,允许一个线程等待某个条件成立,另一个线程在条件满足时通知等待的线程。需要与互斥锁配合使用。
代码示例:生产者-消费者模型
cpp
#include <iostream>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <atomic>
// 共享数据队列,用于在生产者和消费者之间传递数据
std::queue<int> dataQueue;
// 互斥锁,用于保护对共享数据队列的访问
std::mutex mtx;
// 条件变量,用于线程间的同步和通信
std::condition_variable cv;
// 队列的最大容量,防止生产者生产过多数据导致内存耗尽
const int MAX_SIZE = 5;
// 生产结束标志,当所有生产者完成工作后设置为true
bool productionDone = false;
// 原子计数器,记录已完成工作的生产者数量
std::atomic<int> finishedProducers(0);
// 生产者总数量,用于判断所有生产者是否都已完成工作
const int TOTAL_PRODUCERS = 2;
// 生产者函数
void producer(int id, int items) {
// 生产指定数量的物品
for (int i = 0; i < items; ++i) {
// 获取互斥锁,保护对共享数据队列的访问
std::unique_lock<std::mutex> lock(mtx);
// 等待条件:队列有空位。如果队列已满,生产者线程会进入等待状态
cv.wait(lock, []{
return dataQueue.size() < MAX_SIZE;
});
// 生产数据:生成一个唯一的值(生产者ID * 100 + 物品编号)
int value = id * 100 + i;
// 将生产的数据放入队列
dataQueue.push(value);
// 输出生产信息,包括生产者ID、生产的值和当前队列大小
std::cout << "生产者" << id << "生产: " << value
<< " (队列大小: " << dataQueue.size() << ")" << std::endl;
// 释放互斥锁,让其他线程可以访问共享数据
lock.unlock();
// 通知所有等待的线程(消费者和可能等待的生产者)
cv.notify_all();
// 模拟生产需要的时间
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
// 所有生产者完成后才设置结束标志
// 原子递增操作,确保多个生产者可以安全地更新计数器
if (++finishedProducers == TOTAL_PRODUCERS) {
// 获取互斥锁,保护对共享标志的访问
std::lock_guard<std::mutex> lock(mtx);
// 设置生产结束标志
productionDone = true;
// 通知所有等待的线程(消费者),告诉它们生产已经结束
cv.notify_all();
}
}
// 消费者函数
void consumer(int id) {
// 无限循环,直到收到生产结束信号且队列为空
while (true) {
// 获取互斥锁,保护对共享数据队列的访问
std::unique_lock<std::mutex> lock(mtx);
// 等待条件:队列不为空或生产已结束
cv.wait(lock, []{
return !dataQueue.empty() || productionDone;
});
// 检查是否可以结束消费者线程:
// 1. 队列为空(没有数据可消费)
// 2. 生产已结束(不会有新数据产生)
if (dataQueue.empty() && productionDone) {
std::cout << "消费者" << id << "结束" << std::endl;
break; // 退出循环,结束消费者线程
}
// 消费数据:从队列中取出数据
if (!dataQueue.empty()) {
int value = dataQueue.front(); // 获取队首数据
dataQueue.pop(); // 移除队首数据
// 输出消费信息,包括消费者ID、消费的值和当前队列大小
std::cout << "消费者" << id << "消费: " << value
<< " (队列大小: " << dataQueue.size() << ")" << std::endl;
}
// 释放互斥锁,让其他线程可以访问共享数据
lock.unlock();
// 通知所有等待的线程(生产者),告诉它们队列有空位了
cv.notify_all();
// 模拟消费需要的时间
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
int main() {
// 程序开始提示
std::cout << "生产者-消费者模型开始" << std::endl;
// 创建2个生产者线程
// 参数:线程函数,生产者ID,生产物品数量
std::thread producers[2] = {
std::thread(producer, 1, 10), // 生产者1,生产10个物品
std::thread(producer, 2, 8) // 生产者2,生产8个物品
};
// 创建3个消费者线程
// 参数:线程函数,消费者ID
std::thread consumers[3] = {
std::thread(consumer, 1),
std::thread(consumer, 2),
std::thread(consumer, 3)
};
// 等待所有生产者线程完成(阻塞主线程)
for (auto& p : producers) p.join();
// 等待所有消费者线程完成(阻塞主线程)
for (auto& c : consumers) c.join();
// 程序结束提示
std::cout << "生产者-消费者模型结束" << std::endl;
return 0;
}
6. 原子操作
原理
原子操作是不可分割的操作,不会被线程调度机制打断。C++11提供了std::atomic模板类,用于实现无锁的线程安全操作。
代码示例:原子操作
cpp
#include <iostream>
#include <thread>
#include <vector>
#include <atomic>
#include <chrono>
// 原子整数
std::atomic<int> atomicCounter(0);
// 非原子整数
int nonAtomicCounter = 0;
// 原子标志
std::atomic<bool> ready(false);
void atomicIncrement(int iterations) {
while (!ready) { // 等待所有线程就绪
std::this_thread::yield();
}
for (int i = 0; i < iterations; ++i) {
atomicCounter.fetch_add(1, std::memory_order_relaxed);
}
}
void nonAtomicIncrement(int iterations) {
while (!ready) {
std::this_thread::yield();
}
for (int i = 0; i < iterations; ++i) {
++nonAtomicCounter;
}
}
int main() {
const int numThreads = 10;
const int iterations = 100000;
std::vector<std::thread> threads;
// 测试原子操作
std::cout << "测试原子操作..." << std::endl;
atomicCounter = 0;
for (int i = 0; i < numThreads; ++i) {
threads.emplace_back(atomicIncrement, iterations);
}
ready = true; // 开始执行
for (auto& t : threads) {
t.join();
}
threads.clear();
ready = false;
std::cout << "原子计数器结果: " << atomicCounter
<< " (期望: " << numThreads * iterations << ")" << std::endl;
// 测试非原子操作
std::cout << "\n测试非原子操作..." << std::endl;
nonAtomicCounter = 0;
for (int i = 0; i < numThreads; ++i) {
threads.emplace_back(nonAtomicIncrement, iterations);
}
ready = true;
for (auto& t : threads) {
t.join();
}
std::cout << "非原子计数器结果: " << nonAtomicCounter
<< " (期望: " << numThreads * iterations << ")" << std::endl;
// 原子操作的其他用法
std::cout << "\n原子操作的其他用法:" << std::endl;
std::atomic<int> value(10);
// 比较交换操作
int expected = 10;
bool success = value.compare_exchange_strong(expected, 20);
std::cout << "比较交换: " << (success ? "成功" : "失败")
<< ", 新值: " << value << std::endl;
// 原子加载和存储
int loaded = value.load();
std::cout << "加载值: " << loaded << std::endl;
value.store(100);
std::cout << "存储后值: " << value.load() << std::endl;
return 0;
}
7. 线程局部存储
原理
线程局部存储(Thread-Local Storage, TLS)允许每个线程拥有变量的独立副本。C++11引入了thread_local关键字。
代码示例:线程局部存储
cpp
#include <iostream>
#include <thread>
#include <vector>
#include <sstream>
// 线程局部变量
thread_local int threadLocalCounter = 0;
thread_local std::stringstream threadLocalSS;
// 全局变量(所有线程共享)
int globalCounter = 0;
void incrementCounters(int id, int iterations) {
for (int i = 0; i < iterations; ++i) {
++threadLocalCounter; // 每个线程有自己的副本
++globalCounter; // 所有线程共享
threadLocalSS << "线程" << id << ": 局部=" << threadLocalCounter
<< ", 全局=" << globalCounter << "\n";
}
// 输出线程局部结果
std::cout << threadLocalSS.str();
}
int main() {
const int numThreads = 3;
const int iterations = 5;
std::vector<std::thread> threads;
std::cout << "线程局部存储演示:" << std::endl;
std::cout << "==================" << std::endl;
for (int i = 0; i < numThreads; ++i) {
threads.emplace_back(incrementCounters, i + 1, iterations);
}
for (auto& t : threads) {
t.join();
}
std::cout << "\n最终全局计数器: " << globalCounter
<< " (期望: " << numThreads * iterations << ")" << std::endl;
return 0;
}
8. 异步操作(async/future)
原理
std::async启动异步任务,返回std::future对象。std::future用于获取异步操作的结果,可以等待结果、查询状态或获取结果值。
代码示例:异步操作
cpp
#include <iostream>
#include <future>
#include <vector>
#include <chrono>
#include <numeric>
// 计算斐波那契数列(递归版本,用于演示耗时操作)
long long fibonacci(int n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
// 计算数组和
int calculateSum(const std::vector<int>& data) {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
return std::accumulate(data.begin(), data.end(), 0);
}
// 模拟耗时操作
std::string fetchData(const std::string& url) {
std::this_thread::sleep_for(std::chrono::milliseconds(800));
return "数据来自 " + url;
}
int main() {
std::cout << "异步操作演示" << std::endl;
std::cout << "==============" << std::endl;
// 1. 异步执行并等待结果
auto start = std::chrono::high_resolution_clock::now();
std::future<long long> fut1 = std::async(std::launch::async, fibonacci, 40);
std::future<std::string> fut2 = std::async(std::launch::async, fetchData, "http://example.com");
std::vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::future<int> fut3 = std::async(std::launch::async, calculateSum, data);
// 主线程可以做其他工作
std::cout << "主线程做其他工作..." << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(200));
// 获取异步结果(如果未完成会阻塞)
std::cout << "\n获取结果:" << std::endl;
std::cout << "斐波那契(40) = " << fut1.get() << std::endl;
std::cout << "获取数据: " << fut2.get() << std::endl;
std::cout << "数组和 = " << fut3.get() << std::endl;
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "总耗时: " << duration.count() << "ms" << std::endl;
// 2. 检查future状态
std::cout << "\n检查future状态:" << std::endl;
std::future<int> fut4 = std::async(std::launch::deferred, [](){
std::this_thread::sleep_for(std::chrono::milliseconds(100));
return 42;
});
std::cout << "future是否有效: " << (fut4.valid() ? "是" : "否") << std::endl;
// 3. 使用wait_for和wait_until
std::cout << "\n使用wait_for:" << std::endl;
std::future<int> fut5 = std::async(std::launch::async, [](){
std::this_thread::sleep_for(std::chrono::milliseconds(500));
return 100;
});
auto status = fut5.wait_for(std::chrono::milliseconds(100));
if (status == std::future_status::ready) {
std::cout << "任务已完成" << std::endl;
} else if (status == std::future_status::timeout) {
std::cout << "任务超时,仍在运行" << std::endl;
} else if (status == std::future_status::deferred) {
std::cout << "任务延迟执行" << std::endl;
}
// 最终获取结果
std::cout << "最终结果: " << fut5.get() << std::endl;
return 0;
}
9. 线程池实现
原理
线程池管理一组工作线程,避免频繁创建和销毁线程的开销,提高性能。
代码示例:简单线程池
cpp
#include <iostream>
#include <thread>
#include <vector>
#include <queue>
#include <functional>
#include <mutex>
#include <condition_variable>
#include <future>
#include <atomic>
class ThreadPool {
public:
ThreadPool(size_t numThreads) : stop(false) {
for (size_t i = 0; i < numThreads; ++i) {
workers.emplace_back([this] {
while (true) {
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(this->queueMutex);
this->condition.wait(lock, [this] {
return this->stop || !this->tasks.empty();
});
if (this->stop && this->tasks.empty()) {
return;
}
task = std::move(this->tasks.front());
this->tasks.pop();
}
task();
}
});
}
}
template<class F, class... Args>
auto enqueue(F&& f, Args&&... args)
-> std::future<typename std::result_of<F(Args...)>::type> {
using return_type = typename std::result_of<F(Args...)>::type;
auto task = std::make_shared<std::packaged_task<return_type()>>(
std::bind(std::forward<F>(f), std::forward<Args>(args)...)
);
std::future<return_type> res = task->get_future();
{
std::unique_lock<std::mutex> lock(queueMutex);
if (stop) {
throw std::runtime_error("enqueue on stopped ThreadPool");
}
tasks.emplace([task](){ (*task)(); });
}
condition.notify_one();
return res;
}
~ThreadPool() {
{
std::unique_lock<std::mutex> lock(queueMutex);
stop = true;
}
condition.notify_all();
for (std::thread &worker : workers) {
worker.join();
}
}
private:
std::vector<std::thread> workers;
std::queue<std::function<void()>> tasks;
std::mutex queueMutex;
std::condition_variable condition;
bool stop;
};
// 测试函数
int taskFunction(int id, int value) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << "任务" << id << "完成,值: " << value
<< " (线程ID: " << std::this_thread::get_id() << ")" << std::endl;
return value * 2;
}
int main() {
std::cout << "线程池演示" << std::endl;
std::cout << "============" << std::endl;
// 创建线程池(4个工作线程)
ThreadPool pool(4);
std::vector<std::future<int>> results;
// 提交任务
for (int i = 0; i < 8; ++i) {
results.emplace_back(
pool.enqueue(taskFunction, i, i * 10)
);
}
// 获取结果
std::cout << "\n获取任务结果:" << std::endl;
for (int i = 0; i < results.size(); ++i) {
std::cout << "任务" << i << "的结果: " << results[i].get() << std::endl;
}
// 更多任务
std::cout << "\n提交更多任务..." << std::endl;
auto start = std::chrono::high_resolution_clock::now();
std::vector<std::future<int>> moreResults;
for (int i = 0; i < 20; ++i) {
moreResults.emplace_back(
pool.enqueue([i] {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
return i * i;
})
);
}
// 等待所有任务完成
int total = 0;
for (auto& result : moreResults) {
total += result.get();
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "所有任务完成,总和: " << total << std::endl;
std::cout << "总耗时: " << duration.count() << "ms" << std::endl;
return 0;
}
10. 性能考虑与最佳实践
原理
多线程编程需要考虑以下性能因素:
- 线程创建开销:线程创建和销毁成本高
- 上下文切换:过多线程导致频繁上下文切换
- 缓存一致性:多核CPU的缓存同步开销
- 锁竞争:过度使用锁导致性能下降
代码示例:性能对比
cpp
#include <iostream>
#include <thread>
#include <vector>
#include <chrono>
#include <algorithm>
#include <numeric>
#include <mutex>
// 单线程版本
long long sumSingleThread(const std::vector<int>& data) {
auto start = std::chrono::high_resolution_clock::now();
long long sum = std::accumulate(data.begin(), data.end(), 0LL);
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "单线程求和: " << sum << std::endl;
std::cout << "单线程耗时: " << duration.count() << "ms" << std::endl;
return duration.count();
}
// 多线程版本(有锁)
long long sumMultiThreadWithLock(const std::vector<int>& data, int numThreads) {
auto start = std::chrono::high_resolution_clock::now();
std::vector<std::thread> threads;
std::vector<long long> partialSums(numThreads, 0);
std::mutex mtx;
size_t chunkSize = data.size() / numThreads;
for (int i = 0; i < numThreads; ++i) {
threads.emplace_back([i, chunkSize, numThreads, &data, &partialSums, &mtx]() {
size_t startIdx = i * chunkSize;
size_t endIdx = (i == numThreads - 1) ? data.size() : startIdx + chunkSize;
long long localSum = 0;
for (size_t j = startIdx; j < endIdx; ++j) {
localSum += data[j];
}
std::lock_guard<std::mutex> lock(mtx);
partialSums[i] = localSum;
});
}
for (auto& t : threads) {
t.join();
}
long long sum = std::accumulate(partialSums.begin(), partialSums.end(), 0LL);
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "多线程(有锁)求和: " << sum << std::endl;
std::cout << "多线程(有锁)耗时: " << duration.count() << "ms" << std::endl;
return duration.count();
}
// 多线程版本(无锁)
long long sumMultiThreadLockFree(const std::vector<int>& data, int numThreads) {
auto start = std::chrono::high_resolution_clock::now();
std::vector<std::thread> threads;
std::vector<long long> partialSums(numThreads, 0);
size_t chunkSize = data.size() / numThreads;
for (int i = 0; i < numThreads; ++i) {
threads.emplace_back([i, chunkSize, numThreads, &data, &partialSums]() {
size_t startIdx = i * chunkSize;
size_t endIdx = (i == numThreads - 1) ? data.size() : startIdx + chunkSize;
long long localSum = 0;
for (size_t j = startIdx; j < endIdx; ++j) {
localSum += data[j];
}
partialSums[i] = localSum;
});
}
for (auto& t : threads) {
t.join();
}
long long sum = std::accumulate(partialSums.begin(), partialSums.end(), 0LL);
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "多线程(无锁)求和: " << sum << std::endl;
std::cout << "多线程(无锁)耗时: " << duration.count() << "ms" << std::endl;
return duration.count();
}
int main() {
// 生成测试数据
const size_t dataSize = 100000000;
std::vector<int> data(dataSize);
std::generate(data.begin(), data.end(), std::rand);
std::cout << "性能对比测试" << std::endl;
std::cout << "数据大小: " << dataSize << std::endl;
std::cout << "======================" << std::endl;
// 测试不同线程数
for (int numThreads = 1; numThreads <= 8; numThreads *= 2) {
std::cout << "\n使用 " << numThreads << " 个线程:" << std::endl;
if (numThreads == 1) {
sumSingleThread(data);
} else {
long long timeWithLock = sumMultiThreadWithLock(data, numThreads);
long long timeLockFree = sumMultiThreadLockFree(data, numThreads);
std::cout << "锁开销: " << (timeWithLock - timeLockFree) << "ms" << std::endl;
std::cout << "加速比: " << (static_cast<double>(timeWithLock) / timeLockFree)
<< "倍" << std::endl;
}
}
// 最佳实践总结
std::cout << "\n=== 多线程最佳实践 ===" << std::endl;
std::cout << "1. 避免创建过多线程" << std::endl;
std::cout << "2. 尽量使用无锁设计" << std::endl;
std::cout << "3. 使用线程局部存储减少竞争" << std::endl;
std::cout << "4. 合理使用原子操作" << std::endl;
std::cout << "5. 避免虚假共享(false sharing)" << std::endl;
std::cout << "6. 使用线程池管理线程生命周期" << std::endl;
return 0;
}
总结
C++多线程编程涉及的核心概念:
- 线程创建与管理:使用std::thread,注意join和detach的区别
- 同步机制:互斥锁、条件变量、原子操作
- 数据安全:避免数据竞争,正确处理共享数据
- 死锁避免:固定锁顺序、使用std::lock
- 性能优化:减少锁竞争、使用线程局部存储、合理使用原子操作
- 高级特性:异步操作、线程池