我有一段代码,如果底层弱指针过期,
std::atomic<std::weak_ptr<T>>
的行为不会像我预期的那样:
std::atomic<std::weak_ptr<Widget>> ptrAtomicWidget = ...;
std::shared_ptr<Widget> ptrWidget = ptrAtomicWidget.load().lock();
while (ptrWidget == nullptr)
{
ptrWidget = std::make_shared<Widget>();
std::weak_ptr<Widget> ptrExpected; // <--- nullptr
std::weak_ptr<Widget> ptrDesired = ptrWidget;
// Problem Version: Causes an infinite loop when ptrExpected is expired
if (!ptrAtomicWidget.compare_exchange_weak(ptrExpected, ptrDesired))
{
ptrWidget = ptrExpected().lock();
}
// Potential Repair Version: *seems* to work (could alternately move declaration of ptrExpected above while loop)
if (!ptrAtomicWidget.compare_exchange_weak(ptrExpected, ptrDesired)
&& ptrExpected.expired()
&& !ptrAtomicWidget.compare_exchange_weak(ptrExpected, ptrDesired))
{
ptrWidget = ptrExpected().lock();
}
}
我遇到的问题涉及循环体“潜在修复版本”的“似乎有效”部分。修复需要两个不同的过期的weak_ptr在compare_exchange期间可靠地相互比较相等。
std::weak_ptr
没有相等运算符,因此其文档对此保持沉默。我能找到的关于 std::atomic<>
专业化的文档(例如 CPPReference)都没有描述指针过期时比较交换的行为。我不知道它是否恰好适用于我的特定编译器,或者 C++ 标准是否保证它。有人知道它是否能保证按标准工作吗?
您误解了弱指针
compare_exchange
何时成功的条件。
根据atomic<weak_ptr<T>>::compare_exchange_weak
的规范:
效果:如果
等价于p
,则将expected
分配给desired
,并且具有与p
的值对应的同步语义,否则将success
分配给p
并且具有同步语义对应于expected
的值。failure
当两个指针等价时很重要,这也有解释:
备注:如果两个
对象存储相同的指针值并且共享所有权或均为空,则它们是等效的。 弱形式可能会虚假失败。 请参阅[原子.类型.操作]。weak_ptr
初始化为
ptrExpected
的 nullptr
不存储与 ptrAtomicWidget
相同的指针值,因此第一次尝试比较交换总是会失败。
第二个“解决方法循环”“有效”,因为当第一次比较交换不可避免地失败时,
ptrAtomicWidget
的当前值被加载到ptrExpected
中,这使得ptrDesired
可以在第二次尝试时替换它。
对我来说,似乎 && ptrExpected.expired()
可以被删除,因为只有在 ptrWidget == nullptr
时你才会进入循环,这意味着 ptrAtomicWidget
为空或过期。