Chenyj Space
返回博客
·55 分钟

C++ 多线程编程实战:从基础到高级

C++多线程并发异步编程C/C++

线程基础

1. 创建线程

cpp
#include <iostream>
#include <thread>

// 普通函数
void printHello() {
    std::cout << "Hello from thread!" << std::endl;
}

// 带参数的函数
void printNumber(int n) {
    std::cout << "Number: " << n << std::endl;
}

// Lambda 表达式
auto lambda = [](int x) {
    std::cout << "Lambda: " << x << std::endl;
};

// 函数对象
struct Functor {
    void operator()(int x) const {
        std::cout << "Functor: " << x << std::endl;
    }
};

int main() {
    // 1. 普通函数
    std::thread t1(printHello);
    
    // 2. 带参数
    std::thread t2(printNumber, 42);
    
    // 3. Lambda
    std::thread t3(lambda, 100);
    
    // 4. 函数对象
    std::thread t4(Functor(), 200);
    
    // 5. 成员函数
    class MyClass {
    public:
        void func(int x) {
            std::cout << "Member function: " << x << std::endl;
        }
    };
    
    MyClass obj;
    std::thread t5(&MyClass::func, &obj, 300);
    
    // 等待所有线程完成
    t1.join();
    t2.join();
    t3.join();
    t4.join();
    t5.join();
    
    return 0;
}

2. 线程管理

cpp
#include <thread>
#include <chrono>

void longTask() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::cout << "Task completed" << std::endl;
}

int main() {
    std::thread t(longTask);
    
    // 检查线程是否可 join
    if (t.joinable()) {
        std::cout << "Thread is joinable" << std::endl;
    }
    
    // 获取线程 ID
    std::cout << "Thread ID: " << t.get_id() << std::endl;
    
    // 获取硬件并发数
    unsigned int cores = std::thread::hardware_concurrency();
    std::cout << "Hardware concurrency: " << cores << std::endl;
    
    // 分离线程(后台运行)
    t.detach();
    
    // 等待一下,让分离的线程完成
    std::this_thread::sleep_for(std::chrono::seconds(3));
    
    return 0;
}

3. 线程局部存储

cpp
#include <thread>
#include <iostream>

// 线程局部变量
thread_local int threadId = 0;

void worker(int id) {
    threadId = id;  // 每个线程有自己的副本
    std::cout << "Thread " << threadId << " started" << std::endl;
    
    std::this_thread::sleep_for(std::chrono::seconds(1));
    
    std::cout << "Thread " << threadId << " finished" << std::endl;
}

int main() {
    std::thread t1(worker, 1);
    std::thread t2(worker, 2);
    std::thread t3(worker, 3);
    
    t1.join();
    t2.join();
    t3.join();
    
    // 主线程的 threadId 仍然是 0
    std::cout << "Main thread ID: " << threadId << std::endl;
    
    return 0;
}

同步机制

1. 互斥锁(Mutex)

cpp
#include <mutex>
#include <thread>
#include <vector>

class Counter {
private:
    int count = 0;
    std::mutex mtx;
    
public:
    void increment() {
        std::lock_guard<std::mutex> lock(mtx);
        count++;
    }
    
    int getCount() const {
        return count;
    }
};

void worker(Counter& counter, int iterations) {
    for (int i = 0; i < iterations; i++) {
        counter.increment();
    }
}

int main() {
    Counter counter;
    std::vector<std::thread> threads;
    
    // 创建 10 个线程
    for (int i = 0; i < 10; i++) {
        threads.emplace_back(worker, std::ref(counter), 1000);
    }
    
    // 等待所有线程完成
    for (auto& t : threads) {
        t.join();
    }
    
    std::cout << "Final count: " << counter.getCount() << std::endl;
    // 输出:Final count: 10000
    
    return 0;
}

2. 锁的类型

cpp
#include <mutex>
#include <shared_mutex>

std::mutex mtx;
std::recursive_mutex rmtx;
std::timed_mutex tmtx;
std::shared_mutex smtx;

// 1. std::lock_guard - RAII 风格
void func1() {
    std::lock_guard<std::mutex> lock(mtx);
    // 自动解锁
}

// 2. std::unique_lock - 更灵活
void func2() {
    std::unique_lock<std::mutex> lock(mtx, std::defer_lock);
    // 延迟加锁
    
    lock.lock();  // 手动加锁
    // ...
    lock.unlock();  // 手动解锁
    
    lock.lock();  // 可以再次加锁
}

// 3. std::shared_lock - 读写锁
void reader() {
    std::shared_lock<std::shared_mutex> lock(smtx);
    // 多个线程可以同时读
}

void writer() {
    std::unique_lock<std::shared_mutex> lock(smtx);
    // 独占写
}

// 4. std::scoped_lock (C++17) - 同时锁定多个互斥量
void transfer(int& from, int& to, int amount) {
    std::scoped_lock lock(mtx1, mtx2);  // 同时锁定
    from -= amount;
    to += amount;
}

3. 条件变量

cpp
#include <condition_variable>
#include <queue>
#include <thread>

template<typename T>
class ThreadSafeQueue {
private:
    std::queue<T> queue;
    mutable std::mutex mtx;
    std::condition_variable cv;
    
public:
    void push(T value) {
        std::lock_guard<std::mutex> lock(mtx);
        queue.push(std::move(value));
        cv.notify_one();
    }
    
    T pop() {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, [this] { return !queue.empty(); });
        T value = std::move(queue.front());
        queue.pop();
        return value;
    }
    
    bool tryPop(T& value) {
        std::lock_guard<std::mutex> lock(mtx);
        if (queue.empty()) return false;
        value = std::move(queue.front());
        queue.pop();
        return true;
    }
    
    size_t size() const {
        std::lock_guard<std::mutex> lock(mtx);
        return queue.size();
    }
};

// 生产者-消费者模式
void producer(ThreadSafeQueue<int>& queue) {
    for (int i = 0; i < 10; i++) {
        queue.push(i);
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}

void consumer(ThreadSafeQueue<int>& queue) {
    for (int i = 0; i < 10; i++) {
        int value = queue.pop();
        std::cout << "Consumed: " << value << std::endl;
    }
}

int main() {
    ThreadSafeQueue<int> queue;
    
    std::thread t1(producer, std::ref(queue));
    std::thread t2(consumer, std::ref(queue));
    
    t1.join();
    t2.join();
    
    return 0;
}

4. 信号量(C++20)

cpp
#include <semaphore>
#include <thread>
#include <vector>

// C++20 信号量
std::counting_semaphore<10> semaphore(3);  // 最多 3 个并发

void worker(int id) {
    semaphore.acquire();  // 获取许可
    
    std::cout << "Worker " << id << " started" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Worker " << id << " finished" << std::endl;
    
    semaphore.release();  // 释放许可
}

int main() {
    std::vector<std::thread> threads;
    
    for (int i = 0; i < 10; i++) {
        threads.emplace_back(worker, i);
    }
    
    for (auto& t : threads) {
        t.join();
    }
    
    return 0;
}

原子操作

1. 原子变量

cpp
#include <atomic>
#include <thread>
#include <vector>

std::atomic<int> atomicCount(0);
int normalCount = 0;

void increment(int iterations) {
    for (int i = 0; i < iterations; i++) {
        atomicCount++;  // 原子操作
        normalCount++;  // 非原子操作
    }
}

int main() {
    std::vector<std::thread> threads;
    
    for (int i = 0; i < 10; i++) {
        threads.emplace_back(increment, 10000);
    }
    
    for (auto& t : threads) {
        t.join();
    }
    
    std::cout << "Atomic count: " << atomicCount << std::endl;  // 总是 100000
    std::cout << "Normal count: " << normalCount << std::endl;  // 可能小于 100000
    
    return 0;
}

2. 原子操作类型

cpp
#include <atomic>

std::atomic<int> a(0);

// 基本操作
a.store(10);           // 存储
int val = a.load();    // 加载
a.exchange(20);        // 交换

// 读-改-写操作
a++;                   // 原子自增
a--;                   // 原子自减
a += 5;               // 原子加
a -= 3;               // 原子减

// 比较并交换(CAS)
int expected = 10;
bool success = a.compare_exchange_strong(expected, 20);
if (success) {
    // a 从 10 变为 20
} else {
    // expected 被更新为 a 的当前值
}

// 内存顺序
a.store(10, std::memory_order_relaxed);  // 宽松
a.store(10, std::memory_order_acquire);  // 获取
a.store(10, std::memory_order_release);  // 释放
a.store(10, std::memory_order_seq_cst);  // 顺序一致(默认)

3. 无锁数据结构

cpp
#include <atomic>
#include <memory>

// 无锁栈
template<typename T>
class LockFreeStack {
private:
    struct Node {
        T data;
        Node* next;
        Node(T const& data) : data(data), next(nullptr) {}
    };
    
    std::atomic<Node*> head;
    
public:
    LockFreeStack() : head(nullptr) {}
    
    void push(T const& data) {
        Node* newNode = new Node(data);
        newNode->next = head.load();
        
        while (!head.compare_exchange_weak(
            newNode->next, newNode)) {
            // CAS 失败,重试
        }
    }
    
    std::shared_ptr<T> pop() {
        Node* oldHead = head.load();
        
        while (oldHead && !head.compare_exchange_weak(
            oldHead, oldHead->next)) {
            // CAS 失败,重试
        }
        
        if (oldHead) {
            std::shared_ptr<T> result = 
                std::make_shared<T>(std::move(oldHead->data));
            delete oldHead;
            return result;
        }
        
        return nullptr;
    }
    
    ~LockFreeStack() {
        while (pop()) {}
    }
};

异步编程

1. std::async 和 std::future

cpp
#include <future>
#include <iostream>

int compute(int x) {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return x * x;
}

int main() {
    // 异步执行
    std::future<int> result = std::async(std::launch::async, compute, 10);
    
    // 做其他工作
    std::cout << "Doing other work..." << std::endl;
    
    // 获取结果(阻塞)
    int value = result.get();
    std::cout << "Result: " << value << std::endl;
    
    return 0;
}

2. std::promise

cpp
#include <future>
#include <thread>

void producer(std::promise<int> prom) {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    prom.set_value(42);  // 设置结果
}

void consumer(std::future<int> fut) {
    std::cout << "Waiting for result..." << std::endl;
    int value = fut.get();  // 阻塞等待
    std::cout << "Result: " << value << std::endl;
}

int main() {
    std::promise<int> prom;
    std::future<int> fut = prom.get_future();
    
    std::thread t1(producer, std::move(prom));
    std::thread t2(consumer, std::move(fut));
    
    t1.join();
    t2.join();
    
    return 0;
}

3. std::packaged_task

cpp
#include <future>
#include <queue>
#include <thread>

template<typename T>
class TaskQueue {
private:
    std::queue<std::packaged_task<T()>> tasks;
    std::mutex mtx;
    
public:
    void addTask(std::packaged_task<T()> task) {
        std::lock_guard<std::mutex> lock(mtx);
        tasks.push(std::move(task));
    }
    
    std::packaged_task<T()> getTask() {
        std::lock_guard<std::mutex> lock(mtx);
        if (tasks.empty()) return nullptr;
        
        auto task = std::move(tasks.front());
        tasks.pop();
        return task;
    }
};

void worker(TaskQueue<int>& queue) {
    while (true) {
        auto task = queue.getTask();
        if (!task) {
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
            continue;
        }
        
        task();  // 执行任务
    }
}

int main() {
    TaskQueue<int> queue;
    
    // 添加任务
    for (int i = 0; i < 5; i++) {
        std::packaged_task<int()> task([i]() {
            return i * i;
        });
        
        auto future = task.get_future();
        queue.addTask(std::move(task));
        
        // 在其他线程获取结果
        std::cout << "Result: " << future.get() << std::endl;
    }
    
    return 0;
}

4. 协程(C++20)

cpp
#include <coroutine>
#include <iostream>
#include <future>

// 简单的协程任务
template<typename T>
struct Task {
    struct promise_type {
        T value;
        
        Task get_return_object() {
            return Task{
                std::coroutine_handle<promise_type>::from_promise(*this)
            };
        }
        
        std::suspend_never initial_suspend() { return {}; }
        std::suspend_never final_suspend() noexcept { return {}; }
        
        void return_value(T v) {
            value = v;
        }
        
        void unhandled_exception() {
            std::terminate();
        }
    };
    
    std::coroutine_handle<promise_type> handle;
    
    T get() {
        return handle.promise().value;
    }
};

Task<int> computeAsync(int x) {
    co_await std::suspend_always{};
    co_return x * x;
}

int main() {
    auto task = computeAsync(10);
    std::cout << "Result: " << task.get() << std::endl;
    
    return 0;
}

并发模式

1. 线程池

cpp
#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <future>

class ThreadPool {
private:
    std::vector<std::thread> workers;
    std::queue<std::function<void()>> tasks;
    std::mutex mtx;
    std::condition_variable cv;
    bool stop = false;
    
public:
    ThreadPool(size_t numThreads) {
        for (size_t i = 0; i < numThreads; i++) {
            workers.emplace_back([this] {
                while (true) {
                    std::function<void()> task;
                    
                    {
                        std::unique_lock<std::mutex> lock(mtx);
                        cv.wait(lock, [this] {
                            return stop || !tasks.empty();
                        });
                        
                        if (stop && tasks.empty()) {
                            return;
                        }
                        
                        task = std::move(tasks.front());
                        tasks.pop();
                    }
                    
                    task();
                }
            });
        }
    }
    
    ~ThreadPool() {
        {
            std::lock_guard<std::mutex> lock(mtx);
            stop = true;
        }
        
        cv.notify_all();
        
        for (auto& worker : workers) {
            worker.join();
        }
    }
    
    template<typename F, typename... 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> result = task->get_future();
        
        {
            std::lock_guard<std::mutex> lock(mtx);
            
            if (stop) {
                throw std::runtime_error("enqueue on stopped ThreadPool");
            }
            
            tasks.emplace([task]() { (*task)(); });
        }
        
        cv.notify_one();
        
        return result;
    }
};

// 使用示例
int main() {
    ThreadPool pool(4);
    
    std::vector<std::future<int>> results;
    
    for (int i = 0; i < 8; i++) {
        results.emplace_back(
            pool.enqueue([i] {
                std::this_thread::sleep_for(std::chrono::seconds(1));
                return i * i;
            })
        );
    }
    
    for (auto& result : results) {
        std::cout << result.get() << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

2. 读写锁模式

cpp
#include <shared_mutex>
#include <vector>
#include <thread>

class ThreadSafeVector {
private:
    std::vector<int> data;
    mutable std::shared_mutex mtx;
    
public:
    void push_back(int value) {
        std::unique_lock<std::shared_mutex> lock(mtx);
        data.push_back(value);
    }
    
    int get(size_t index) const {
        std::shared_lock<std::shared_mutex> lock(mtx);
        return data.at(index);
    }
    
    size_t size() const {
        std::shared_lock<std::shared_mutex> lock(mtx);
        return data.size();
    }
    
    void set(size_t index, int value) {
        std::unique_lock<std::shared_mutex> lock(mtx);
        data.at(index) = value;
    }
};

// 多读者-单写者模式
void reader(const ThreadSafeVector& vec, int id) {
    for (int i = 0; i < 100; i++) {
        int value = vec.get(i % vec.size());
        // 处理数据
    }
}

void writer(ThreadSafeVector& vec, int id) {
    for (int i = 0; i < 10; i++) {
        vec.push_back(id * 100 + i);
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}

性能优化

1. 避免伪共享

cpp
#include <thread>
#include <vector>

// 伪共享问题
struct BadCounter {
    int count1;
    int count2;  // 可能在同一缓存行
};

// 解决方案:填充
struct GoodCounter {
    alignas(64) int count1;  // 64 字节对齐
    alignas(64) int count2;
};

// 或者使用 C++17 硬件干扰大小
struct Counter {
    alignas(std::hardware_destructive_interference_size) int count1;
    alignas(std::hardware_destructive_interference_size) int count2;
};

2. 无锁编程技巧

cpp
#include <atomic>

// 使用原子操作代替锁
std::atomic_flag lock = ATOMIC_FLAG_INIT;

void acquire() {
    while (lock.test_and_set(std::memory_order_acquire)) {
        // 自旋等待
    }
}

void release() {
    lock.clear(std::memory_order_release);
}

// 使用原子计数器
std::atomic<size_t> counter(0);

size_t getNextId() {
    return counter.fetch_add(1, std::memory_order_relaxed);
}

常见问题与解决方案

1. 竞态条件

cpp
// 问题
int shared = 0;

void bad() {
    shared++;  // 非原子操作
}

// 解决方案
std::atomic<int> safe_shared(0);

void good() {
    safe_shared++;  // 原子操作
}

2. 死锁

cpp
// 问题
std::mutex mtx1, mtx2;

void thread1() {
    std::lock_guard<std::mutex> lock1(mtx1);
    std::lock_guard<std::mutex> lock2(mtx2);
}

void thread2() {
    std::lock_guard<std::mutex> lock2(mtx2);
    std::lock_guard<std::mutex> lock1(mtx1);
}

// 解决方案
void safe_thread1() {
    std::scoped_lock lock(mtx1, mtx2);  // C++17
}

// 或者固定顺序
void safe_thread2() {
    std::lock_guard<std::mutex> lock1(mtx1);
    std::lock_guard<std::mutex> lock2(mtx2);
}

3. 线程饥饿

cpp
// 使用公平锁
class FairLock {
private:
    std::mutex mtx;
    std::condition_variable cv;
    int serving = 0;
    int ticket = 0;
    
public:
    void lock() {
        std::unique_lock<std::mutex> lock(mtx);
        int myTicket = ticket++;
        cv.wait(lock, [&] { return myTicket == serving; });
    }
    
    void unlock() {
        std::lock_guard<std::mutex> lock(mtx);
        serving++;
        cv.notify_all();
    }
};

最佳实践总结

设计原则

  1. 最小化共享:减少线程间共享的数据
  2. 不可变性:尽量使用 const 和不可变对象
  3. RAII:使用 RAII 管理锁和资源
  4. 高层抽象:优先使用线程池、future 等高层抽象

性能考虑

  1. 避免过度同步:只在必要时加锁
  2. 使用原子操作:对于简单操作,原子操作比锁更高效
  3. 考虑无锁结构:对于高性能场景
  4. 注意缓存行:避免伪共享

调试技巧

  1. ThreadSanitizer:检测数据竞争
  2. 死锁检测:使用 std::lock 避免死锁
  3. 日志记录:记录线程操作
  4. 单元测试:多线程代码的测试

总结

核心概念

  1. 线程管理:创建、同步、销毁
  2. 同步机制:互斥锁、条件变量、原子操作
  3. 异步编程:future、promise、协程
  4. 并发模式:线程池、生产者-消费者、读写锁

常见问题

  • 竞态条件
  • 死锁
  • 线程饥饿
  • 伪共享

学习资源


延伸阅读