跳转到内容

更多 C++ 惯用法/线程安全写时复制

来自维基教科书,开放的书籍,为一个开放的世界

线程安全写时复制

[编辑 | 编辑源代码]

允许对象以并发安全的方式快速访问,无需必要复制,最好是在没有锁的情况下。

也称为

[编辑 | 编辑源代码]

许多应用程序允许在运行时进行配置更改,但并发地将更改应用到系统需要快速同步原语。普遍存在的互斥,可能会阻塞正在执行的线程,对于像游戏引擎这样的延迟敏感应用程序不是可行的解决方案。此惯用法是流行的互斥的替代方案,适用于读取很常见而写入很少见的情况。读取路径很快并且从不阻塞,但增量写入可能需要互斥。

解决方案和示例代码

[编辑 | 编辑源代码]
#pragma once

#include <atomic>
#include <memory>
#include <folly/concurrency/AtomicSharedPtr.h>

template <typename ValT, typename KeyT>
class ThreadSafeCowPtr {
public:
    using DataT = std::unordered_map<KeyT, ValT>;

    ThreadSafeCowPtr() : data{std::make_shared<DataT>()} {
        assert(data.is_lock_free());
    }   

    std::shared_ptr<DataT> read() {
        return data.load();
    }   

    void write(std::shared_ptr<DataT> new_ptr) {
        // does not require mutex if we don't care about previous content
        data.store(new_ptr);
    }   

    // Copy on write
    void update(const KeyT& key, const ValT& val) {
        // note that update() operation requires mutex to guarantee that
        // multiple threads calling update() won't drop any data
        std::lock_guard<std::mutex> lock(update_mut);
        auto new_ptr = std::make_shared<DataT>(*this->read()); // assume valid
        new_ptr->emplace(key, val);
        this->write(new_ptr);
    }   

    private:
    std::mutex update_mut;
    folly::atomic_shared_ptr<DataT> data;
};

此实现依赖于以下事实:库 folly 提供无锁原子共享指针。并非所有库都提供无锁智能指针,即使原子特化可用。即,c++ 标准库不提供无锁原子指针[1]。以下代码将不会评估为真。

std::shared_ptr<int> obj;
assert(std::atomic(obj).is_lock_free()); // fails as of c++20

已知用法

[编辑 | 编辑源代码]
[编辑 | 编辑源代码]

参考文献

[编辑 | 编辑源代码]
  1. Timour, Doumler. "一个无锁原子 shared_ptr ACCU 2022" (PDF).
华夏公益教科书