C++多线程全面详解

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. 死锁与避免

原理

死锁发生在两个或多个线程相互等待对方释放锁时。常见解决方案:

  1. 总是以相同顺序获取锁
  2. 使用std::lock同时锁定多个互斥量
  3. 使用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. 性能考虑与最佳实践

原理

多线程编程需要考虑以下性能因素:

  1. 线程创建开销:线程创建和销毁成本高
  2. 上下文切换:过多线程导致频繁上下文切换
  3. 缓存一致性:多核CPU的缓存同步开销
  4. 锁竞争:过度使用锁导致性能下降

代码示例:性能对比

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++多线程编程涉及的核心概念:

  1. 线程创建与管理:使用std::thread,注意join和detach的区别
  2. 同步机制:互斥锁、条件变量、原子操作
  3. 数据安全:避免数据竞争,正确处理共享数据
  4. 死锁避免:固定锁顺序、使用std::lock
  5. 性能优化:减少锁竞争、使用线程局部存储、合理使用原子操作
  6. 高级特性:异步操作、线程池
相关推荐
lihongli0002 小时前
【工程实战】Win11 + Ubuntu20.04 + Ubuntu24.04 三系统长期稳定安装方案(含避坑指南)
开发语言
黄宝康2 小时前
sublimetext 运行python程序
开发语言·python
m0_748250032 小时前
C++ 官方文档与标准
开发语言·c++
zh_xuan3 小时前
kotlin 类继承的语法2
开发语言·kotlin
matlabgoodboy3 小时前
程序代做python代编程matlab定制代码编写C++代写plc设计java帮做
c++·python·matlab
DYS_房东的猫3 小时前
《 C++ 零基础入门教程》第6章:模板与 STL 算法 —— 写一次,用万次
开发语言·c++·算法
诗意地回家3 小时前
淘宝小游戏反编译
开发语言·前端·javascript
wangkay883 小时前
【Java 转运营】Day04:抖音新号起号前准备全指南
java·开发语言·新媒体运营
点云SLAM3 小时前
C++ 静态初始化顺序问题(SIOF)和SLAM / ROS 工程实战问题
开发语言·c++·slam·静态初始化顺序问题·工程实战技术·c++static 关键字