更多 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
- ↑ Timour, Doumler. "一个无锁原子 shared_ptr ACCU 2022" (PDF).