假设我想实现一个自己的std :: atomic类型(当然我不想要,我只是出于好奇而问)。我该如何实现加载方法?我的猜测类似于以下内容(m_mutex
是用于锁定操作的互斥锁,m_object
是原子当前持有的对象:
T& load() {
std::lock_guard<std::mutex> lck(m_mutex);
return m_object;
}
显然,这不起作用,因为在调用a.load().doSomething()
时,对doSomething
的调用不会受到保护,因为m_object
它已经从load
返回并且没有被load
保护再次锁定。
我想问两个问题:
答案 0 :(得分:1)
如何实现正确的加载版本?
template <typename T>
class atomic {
public:
static_assert(std::is_trivially_copyable<T>(),
"atomic<T> requires trivially copyable T");
// ...
// Ignores the memory ordering parameter and always provides
// sequential consistency. This is *correct*, but possibly
// sacrifices performance.
T load(std::memory_order) const noexcept {
std::lock_guard<std::mutex> guard{mtx_};
return t_;
}
private:
std::mutex mtx_;
T t_;
};
使用互斥锁而不是使用无锁数据结构实现负载的简单技术是什么?
c ++中的无锁数据结构是使用无锁原子对象实现的。在标准C ++的范围内,无法实现无锁原子对象;他们需要来自编译器内在函数和/或特定于平台的内联汇编的支持,以便通过适当的排序保证来执行原子内存操作。在您选择的标准库中查看std::atomic
的实现,以获取许多血腥细节。
答案 1 :(得分:1)
显然,这不会起作用,因为在打电话时 a.load()。doSomething()对doSomething的调用不受保护 因为m_object它已经从加载返回而没有被保护 锁定守卫了。
这是此接口不可避免的副作用,任何实现都无法更改。如果您不仅要加载它,还要保护它上面的其他随机功能,那么这就是一个完全不同的鱼。
如果您认为std::atomic
这是安全的,那就不是。