[英]Atomic exchange of two std::atomic<T*> objects in a lock-free manner in C++11?
The following code is a skeleton of an atomic pointer class taken from a simulated annealing application in the PARSEC benchmark suite for shared-memory multiprocessors . 以下代码是一个原子指针类的框架,它取自PARSEC基准套件中用于共享内存多处理器的模拟退火应用程序。
In that application, the central data structure is a graph (more specifically, a netlist of an integrated circuit). 在该应用中,中央数据结构是图(更具体地,集成电路的网表)。 Each node in the graph has an attribute indicating its physical location.
图中的每个节点都有一个指示其物理位置的属性。 The algorithm spawns many threads and each thread repeatedly and randomly selects two nodes and exchanges their physical locations if that results in better routing cost for the chip.
该算法产生许多线程并且每个线程重复并随机选择两个节点并交换它们的物理位置,如果这导致芯片的更好的路由成本。
Because the graph is huge and any pair of nodes can be chosen by each thread, the only workable solution is a lock-free concurrent data structure (CDS). 因为图形很大并且每个线程都可以选择任何一对节点,所以唯一可行的解决方案是无锁并发数据结构(CDS)。 That's why the following
AtomicPtr
class is crucial (it is used to atomically exchange pointers to two physical location objects in a lock-free manner). 这就是为什么以下
AtomicPtr
类是至关重要的(它用于以无锁方式原子地交换指向两个物理位置对象的指针)。 The function atomic_load_acq_ptr()
is a defined in assembly code and corresponds closely to std::atomic<T*>::load(memory_order_acquire)
. 函数
atomic_load_acq_ptr()
是在汇编代码中定义的,并且与std::atomic<T*>::load(memory_order_acquire)
紧密对应。
I want to implement that CDS using C++11 atomics. 我想用C ++ 11原子实现该CDS。
template <typename T>
class AtomicPtr {
private:
typedef long unsigned int ATOMIC_TYPE;
T *p __attribute__ ((aligned (8)));
static const T *ATOMIC_NULL;
inline T *Get() const {
T *val;
do {
val = (T *)atomic_load_acq_ptr((ATOMIC_TYPE *)&p);
} while(val == ATOMIC_NULL);
return val;
}
inline void Swap(AtomicPtr<T> &X) {
// Define partial order in which to acquire elements to prevent deadlocks
AtomicPtr<T> *first;
AtomicPtr<T> *last;
// Always process elements from lower to higher memory addresses
if (this < &X) {
first = this;
last = &X;
} else {
first = &X;
last = this;
}
// Acquire and update elements in correct order
T *valFirst = first->Checkout(); // This sets p to ATOMIC_NULL so all Get() calls will spin.
T *valLast = last->PrivateSet(valFirst);
first->Checkin(valLast); // This restores p to valLast
}
};
The std::atomic<T*>::exchange()
method can only be used to exchange a bare T*
pointer with a std::atomic<T*>
object. std::atomic<T*>::exchange()
方法只能用于与std::atomic<T*>
对象交换裸T*
指针。 How to do exchange of two std::atomic<T*>
objects in a lock-free manner? 如何以无锁方式交换两个
std::atomic<T*>
对象?
What I can think of is that the AtomicPtr
class below can itself be based on std::atomic<T*>
by declaring: 我能想到的是下面的
AtomicPtr
类本身可以基于std::atomic<T*>
来声明:
std::atomic<T*> p;
and replacing all atomic_load_acq_ptr()
calls by std::atomic<T*>::load(memory_order_acquire)
and replacing all atomic_store_rel_ptr()
calls by std::atomic<T*>::store(memory_order_release)
. 和更换所有
atomic_load_acq_ptr()
通过调用std::atomic<T*>::load(memory_order_acquire)
和替换所有atomic_store_rel_ptr()
通过调用std::atomic<T*>::store(memory_order_release)
But my first thought was that std::atomic<T*>
should replace AtomicPtr
itself and there may be a clever way to exchange std::atomic<T*>
objects directly. 但我的第一个想法是
std::atomic<T*>
应该取代AtomicPtr
本身,并且可能有一种聪明的方式直接交换std::atomic<T*>
对象。 Any thoughts? 有什么想法吗?
It seems to me that the simpler way to get what you wish is to replicate the logic that you have seen here. 在我看来,获得你想要的更简单的方法是复制你在这里看到的逻辑。
The problem is that there is no possibility to get an atomic operations across two atomic objects, so you have to follow a procedure: 问题是不可能跨两个原子对象获取原子操作,因此您必须遵循以下过程:
This is, of course, quite imperfect: 当然,这非常不完美:
However it should work relatively well in practice in the case of only 2 objects (and thus one to lock). 然而,在仅有2个物体(因此锁定一个物体)的情况下,它在实践中应该相对较好地工作。
Finally, I have two remarks: 最后,我有两个评论:
0x01
usually works well for pointers. 0x01
通常适用于指针。 std::atomic<T*>
be lock-free, you can check this for your particular implementation and platform with std::atomic<T*>::is_lock_free()
. std::atomic<T*>
是无锁的,你可以使用std::atomic<T*>::is_lock_free()
来检查你的特定实现和平台。 The closest you can come without a spinlock is: 没有螺旋锁的最接近的是:
std::atomic<T> a;
std::atomic<T> b;
a = b.exchange(a);
Which is thread safe for b
. 哪个是
b
线程安全。
a
may not be concurrently accessed. a
可能无法同时访问。
Have you checked out a CAS (compare and swap ) operation? 你检查了CAS(比较和交换)操作吗?
std::atomic<T*> v;
while(!v.compare_exchange_weak(old_value,new_value, std::memory_order_release, memory_order_relaxed))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.