使用现代C++标准库工具

C++11及后续版本提供了丰富的并发编程工具,包括<thread><mutex><atomic><future>等头文件。优先使用std::thread而非平台相关API,使用std::mutex配合std::lock_guardstd::unique_lock实现资源同步。

避免原始锁的暴露

通过RAII机制管理锁资源,确保异常安全。std::lock_guard在构造时自动加锁,析构时自动解锁;std::unique_lock提供更灵活的锁定控制。例如:

std::mutex mtx;
{
    std::lock_guard<std::mutex> lock(mtx);
    // 临界区代码
} // 自动解锁

善用原子操作

对于简单的计数器或标志位,使用std::atomic避免锁开销。原子操作提供无锁编程支持,例如:

std::atomic<int> counter{0};
counter.fetch_add(1, std::memory_order_relaxed);

任务并行替代线程管理

使用更高层次的std::asyncstd::future抽象任务并行。这允许运行时系统管理线程池,而非手动创建线程:

auto future = std::async(std::launch::async, []{
    return compute_heavy_task();
});
auto result = future.get();

注意内存顺序

理解内存模型和内存序(std::memory_order)。默认使用std::memory_order_seq_cst保证顺序一致性,但在性能关键场景可考虑更宽松的排序:

std::atomic<bool> ready{false};
// 写端
ready.store(true, std::memory_order_release);
// 读端
while(!ready.load(std::memory_order_acquire));

避免死锁

按固定顺序获取多个锁,或使用std::lock同时锁定多个互斥量。C++17的std::scoped_lock支持多锁RAII管理:

std::mutex mtx1, mtx2;
{
    std::scoped_lock lock(mtx1, mtx2); // 自动解决死锁风险
    // 操作共享资源
}

使用条件变量正确

std::condition_variable需配合谓词使用,防止虚假唤醒。示例模式:

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

// 等待端
{
    std::unique_lock<std::mutex> lock(mtx);
    cv.wait(lock, []{ return data_ready; });
}

// 通知端
{
    std::lock_guard<std::mutex> lock(mtx);
    data_ready = true;
}
cv.notify_one();

线程局部存储

对于不需要共享的数据,使用thread_local关键字或std::thread::id识别线程。这能避免同步开销:

thread_local int thread_specific_data = 0;

限制线程数量

通过std::thread::hardware_concurrency()获取硬件支持的线程数,避免过度订阅。线程池实现应基于工作窃取(work-stealing)等高效调度策略。

异常处理

确保线程函数内部的异常被捕获处理,否则可能导致程序终止。可通过std::promise传递异常到主线程:

std::promise<void> prom;
auto future = prom.get_future();
std::thread([&prom]{
    try {
        // 可能抛出异常的操作
        prom.set_value();
    } catch(...) {
        prom.set_exception(std::current_exception());
    }
}).detach();

try {
    future.get();
} catch(const std::exception& e) {
    // 处理异常
}

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐