现代 C++ 并发编程

一、C++ 并发编程的演进历程
1. 传统多线程编程的困境

在 C++11 之前,开发者依赖操作系统 API(如 Windows 线程或 POSIX 线程)进行并发编程,面临以下挑战:

  • 平台依赖性:代码难以跨平台移植
  • 资源管理复杂:手动创建 / 销毁线程,缺乏 RAII 机制
  • 同步原语简陋:条件变量、互斥锁实现不一致

典型的 POSIX 线程示例:

#include <pthread.h>

void* thread_func(void* arg) {
    // 线程执行逻辑
    return nullptr;
}

int main() {
    pthread_t thread;
    pthread_create(&thread, nullptr, thread_func, nullptr);
    pthread_join(thread, nullptr); // 等待线程结束
    return 0;
}
2. C++11 的并发革命

C++11 标准库引入了<thread><mutex><condition_variable>等头文件,提供了标准化的并发编程工具:

  • 线程管理std::thread类封装线程创建与销毁
  • 同步原语std::mutexstd::lock_guardstd::condition_variable
  • 原子操作std::atomic模板类提供无锁编程支持
二、基础线程操作与管理
1. 线程的创建与启动
#include <iostream>
#include <thread>

void worker() {
    std::cout << "Worker thread ID: " << std::this_thread::get_id() << std::endl;
}

int main() {
    std::thread t(worker); // 创建并启动线程
    
    // 主线程继续执行
    std::cout << "Main thread ID: " << std::this_thread::get_id() << std::endl;
    
    t.join(); // 等待子线程完成
    // t.detach(); // 分离线程,使其在后台运行
    return 0;
}
2. 线程传参与返回值
#include <thread>
#include <future>

// 带参数的线程函数
void sum(int a, int b, std::promise<int>& result) {
    result.set_value(a + b);
}

int main() {
    std::promise<int> prom;
    std::future<int> fut = prom.get_future();
    
    std::thread t(sum, 3, 4, std::ref(prom));
    t.join();
    
    std::cout << "Sum: " << fut.get() << std::endl;
    return 0;
}
3. 线程池的实现思路
#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <future>

class ThreadPool {
public:
    explicit ThreadPool(size_t num_threads) {
        for (size_t i = 0; i < num_threads; ++i) {
            workers.emplace_back([this] {
                while (true) {
                    std::function<void()> task;
                    {
                        std::unique_lock<std::mutex> lock(this->queue_mutex);
                        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();
                }
            });
        }
    }
    
    ~ThreadPool() {
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            stop = true;
        }
        condition.notify_all();
        for (std::thread& worker : workers) {
            worker.join();
        }
    }
    
    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(queue_mutex);
            if (stop) throw std::runtime_error("enqueue on stopped ThreadPool");
            tasks.emplace([task]() { (*task)(); });
        }
        condition.notify_one();
        return res;
    }

private:
    std::vector<std::thread> workers;
    std::queue<std::function<void()>> tasks;
    std::mutex queue_mutex;
    std::condition_variable condition;
    bool stop = false;
};
三、同步原语与线程安全
1. 互斥锁与锁守卫
#include <mutex>

class Counter {
private:
    int value;
    std::mutex mtx;

public:
    void increment() {
        std::lock_guard<std::mutex> lock(mtx);
        ++value; // 线程安全的自增操作
    }
    
    // 更灵活的锁管理
    int get_value() const {
        std::unique_lock<std::mutex> lock(mtx);
        // 可以在此处解锁:lock.unlock();
        return value;
    }
};
2. 条件变量的使用
#include <condition_variable>

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void worker_thread() {
    std::unique_lock<std::mutex> lock(mtx);
    cv.wait(lock, []{ return ready; }); // 等待通知
    // 执行任务...
}

int main() {
    std::thread t(worker_thread);
    
    // 准备数据...
    {
        std::lock_guard<std::mutex> lock(mtx);
        ready = true;
    }
    cv.notify_one(); // 通知工作线程
    
    t.join();
    return 0;
}
3. 读写锁(C++17)
#include <shared_mutex>

class DataCache {
private:
    mutable std::shared_mutex rw_mutex;
    std::map<std::string, std::string> cache;

public:
    std::string read(const std::string& key) const {
        std::shared_lock<std::shared_mutex> lock(rw_mutex); // 共享锁(读锁)
        return cache.at(key);
    }
    
    void write(const std::string& key, const std::string& value) {
        std::unique_lock<std::shared_mutex> lock(rw_mutex); // 排他锁(写锁)
        cache[key] = value;
    }
};
四、原子操作与无锁编程
1. 原子变量基础
#include <atomic>

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

void increment() {
    counter.fetch_add(1, std::memory_order_relaxed);
    // 等价于 counter++,但为原子操作
}
2. 内存序的选择
std::atomic<bool> ready(false);
int data = 0;

void producer() {
    data = 100; // 1. 写入数据
    ready.store(true, std::memory_order_release); // 2. 释放操作
}

void consumer() {
    while (!ready.load(std::memory_order_acquire)); // 3. 获取操作
    // 4. 此时保证能看到data=100
}
3. 无锁队列实现示例
#include <atomic>

template<typename T>
class LockFreeQueue {
private:
    struct Node {
        T data;
        std::atomic<Node*> next;
        Node(const T& value) : data(value), next(nullptr) {}
    };
    
    std::atomic<Node*> head;
    std::atomic<Node*> tail;

public:
    LockFreeQueue() : head(nullptr), tail(nullptr) {}
    
    void enqueue(const T& value) {
        Node* new_node = new Node(value);
        Node* old_tail = tail.load();
        while (!tail.compare_exchange_weak(old_tail, new_node)) {}
        if (old_tail) old_tail->next = new_node;
        else head = new_node;
    }
    
    bool dequeue(T& value) {
        Node* old_head = head.load();
        while (old_head) {
            Node* next = old_head->next;
            if (head.compare_exchange_weak(old_head, next)) {
                value = old_head->data;
                delete old_head;
                return true;
            }
        }
        return false;
    }
};
五、C++20 协程:轻量级并发方案
1. 协程基础概念
  • 暂停点:协程可在co_await处暂停执行,保存状态
  • 无栈协程:C++ 协程基于状态机实现,无需传统线程栈
  • 返回类型std::taskgenerator等包装协程结果
2. 简单协程示例
#include <coroutine>
#include <iostream>
#include <future>

// 协程返回类型
template<typename T>
struct Task {
    struct promise_type {
        T value_;
        Task get_return_object() { return {}; }
        std::suspend_never initial_suspend() { return {}; }
        std::suspend_never final_suspend() noexcept { return {}; }
        void return_value(const T& value) { value_ = value; }
        void unhandled_exception() {}
    };
};

// 异步任务
Task<int> async_task() {
    std::cout << "协程开始执行" << std::endl;
    co_await std::suspend_always{}; // 暂停点
    std::cout << "协程恢复执行" << std::endl;
    co_return 42;
}

int main() {
    auto task = async_task();
    // 协程执行到第一个暂停点
    std::cout << "主线程继续执行" << std::endl;
    // 恢复协程执行(实际应用中通常由调度器完成)
    return 0;
}
3. 协程的实际应用
// 异步文件读取协程
Task<std::string> read_file_async(const std::string& path) {
    auto file = co_await open_file_async(path);
    auto content = co_await read_all_async(file);
    co_await close_file_async(file);
    co_return content;
}
六、并发编程最佳实践
  1. 避免共享状态

    • 优先使用线程局部存储(thread_local
    • 通过消息传递而非共享内存通信(如使用线程安全队列)
  2. 细粒度锁设计

    • 减小锁的粒度,避免长时间持有锁
    • 使用读写锁优化读多写少的场景
  3. 异常安全

    • 使用std::lock_guardstd::unique_lock等 RAII 包装器
    • 确保异常发生时资源能正确释放
  4. 性能调优

    • 使用无锁数据结构减少线程竞争
    • 避免不必要的同步操作,采用原子变量替代互斥锁
七、并发编程工具链
  1. 性能分析工具

    • Valgrind:检测内存泄漏和数据竞争
    • perf:分析 CPU 性能瓶颈
    • Thread Sanitizer:检测线程竞争问题
  2. 并发模式库

    • Intel TBB:提供并行算法和任务调度框架
    • Folly:Facebook 开源的 C++ 扩展库,含多种并发组件
总结

现代 C++ 的并发编程能力已从早期的简陋实现发展为功能完备的体系:

  • 线程管理std::thread提供跨平台线程操作
  • 同步原语mutexcondition_variable等保障线程安全
  • 原子操作:提供高效无锁编程支持
  • 协程:轻量级并发方案,适用于高并发 IO 场景

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mxpan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值