今天我需要做的原子操作相当棘手,我需要一些帮助。简而言之,我想原子地执行以下操作:
/* begin of atomicity */
if (--counter == 0)
{
Widget* widget = nullptr;
swap(some_widget, widget);
/* end of atomicity */
// use local 'widget' here
}
/* end of atomicity */
我知道C ++ 11原子和内在函数。前面的代码只是伪,旨在显示我想要原子地发生的事情。我知道一个解决方案是让计数器有一个" Transition" state(例如0xFFFFFFFF)并且仅将其值更改为先转换到此转换,然后更改为新值:它基本上类似于自旋锁。关于如何有效地 的任何想法(即没有自旋锁,没有互斥,也可能没有等待)?
非常感谢。
答案 0 :(得分:0)
假设C ++ 11,你想做一个原子fetch_and_add,然后使用结果。
例如:
std::atomic<int> counter = 20;
if (counter.fetch_sub(1) == 1) /* return value is old value */
{
Widget* widget = nullptr;
swap(some_widget, widget);
}
如果跨线程使用计数器,我们需要确保编译器和硬件不会弄乱内存排序。
std::atomic<>.fetch_sub()
的默认排序是std::memory_order_seq_cst
,这样做会很好,但如果你想获胜,例如ARM64,您可以减少到std::memory_order_acquire
。
为了使交换操作符合上述方案,我们需要一个锁。 有趣的是,这适合&#34;双重检查锁&#34;模式,这是一些人不赞成但在这里相当有用 - 对于大多数操作你不会受到锁定,但是当它重要时,它就在那里。 以下是:
std::atomic<int> counter = 20;
std::mutex counterLock;
std::atomic<Widget*> some_widget;
...
if (counter.load(std::memory_order_acquire) > 5) /* first check whether we're "close enough" */
{
/* only lock when needed */
std::lock_guard<std::mutex> templock(counterLock); /* free lock whenever we exit this scope */
if (counter.fetch_sub(1) == 1) /* return value is old value */
{
Widget* oldwidgetpr = some_widget.exchange(nullptr, std::memory_ordering_release);
/* do whatever you need to do with the oldwidgetptr */
}
}
else
counter.fetch_sub(1);