c++ - 如何以原子方式复制不可复制的类型
问题描述
我正在写一个 Atom 类,因为类型 T 不是可简单复制的。我想知道我下面的 load() 和 store() 实现是否会导致竞争条件。
class Atom {
// Assumptions:
// 1. atomic<T> is not natively supported
// 2. T does not contain any interior-pointers (pointers pointing to a field
// in the structure or the struct itself)
// 3. T is neither trivially-copyable nor trivially-destructible
// 4. T's copy constructor is thread-safe; i.e. The object being copied is not
// mutated without synchronization.
using LT = typename std::aligned_storage<sizeof(T), alignof(T)>::type;
spin::MRSWLock<T> locked_object; // a multiple-reader-single-writer spinlock
template<class... Args, class = std::enable_if_t<std::is_constructible_v<T, Args...>>>
explicit Atom(Args&&... args): locked_object(std::forward<Args>(args)...) {}
T load() const {
LT l;
{
auto x = locked_object.read(); // get read lock
// make a bitwise/shallow copy of object
l = *reinterpret_cast<const LT*>(&*x);
} // object unlocked here when x goes out of scope
// make a deep copy of the object here by calling copy constructor.
return T(*reinterpret_cast<const T*>(&l));
}
template<class... Args, class = std::enable_if_t<std::is_constructible_v<T, Args...>>>
void store(Args&&... args) const {
LT l, m;
// construct the new object
new (&l) T(std::forward<Args>(args)...);
{
auto x = locked_object.write(); // get write lock
// make a bitwise-copy of the current object
m = *reinterpret_cast<const LT*>(&*x);
// make bitwise-assign of the new value of the object
*reinterpret_cast<LT*>(&*x) = l;
}// object unlocked here as x goes out of scope
// destroy old object here.
reinterpret_cast<T*>(&m)->~T();
}
};
如果可能出现竞争条件,有没有办法在对象被锁定时复制对象而不调用其复制构造函数?
解决方案
您的代码充满了错误。
m = reinterpret_cast<const LT*>(&*x);
在这里,您将指针分配给非指针。
l = reinterpret_cast<const LT*>(&*x);
然后再次。
如果我试图读懂您的想法并忽略您的代码,而是按照注释进行操作,那么不,使用对象的字节副本作为不可轻易复制的对象是 UB。时期。
复制一个不可复制的对象需要运行它的复制构造函数。
销毁不存在的对象
reinterpret_cast<T*>(&m)->~T();
也是UB。
您似乎陷入了只有天真地映射到汇编指令才能确定 C++ 代码的含义和正确性的陷阱。
C++ 是根据抽象机器定义的,该机器知道对象在哪里以及它们的类型是什么。编译器可能不完全了解此信息,它可能不会直接显示在生成的程序集中,但违反其规则是 UB,编译器可以并且确实使用该信息来更改生成的程序集,以破坏您的程序,如果您打破常规。
最糟糕的是,您不知道哪个编译器更新或标志会破坏您通过测试的代码,可能以像时间旅行这样的疯狂方式。(是的,在 C++ 中,UB 被允许在运行之前进行时间旅行和破坏代码)。
不要使用UB。维护成本太高了。
推荐阅读
- javascript - jQuery 嵌套循环
- delphi - 是否有将 DBGRID 大量数据导出为 CSV 和 XLS 的功能?
- tsql - 如何一次旋转多个值列?
- excel - 用户输入数据到图表上
- python - 使用 PyInstaller 冻结带有子命令的 Python `click` 应用程序
- ios - YPDrawSignatureView - TableView 单元格重复
- grails - Grails login.gsp 不存在
- javascript - 如何找到应用程序已停止的错误反应本机
- java - Java 从不同的项目导入一个类但具有相同的包名
- load-balancing - 如何在 Nifi 中配置连接,使公共属性的数据流到同一个节点集群 Nifi 环境