Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/xml/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 两个std::Atomic的原子交换<;T*>;以无锁方式在C++;11?_C++_C++11_Atomic_Atomicity - Fatal编程技术网

C++ 两个std::Atomic的原子交换<;T*>;以无锁方式在C++;11?

C++ 两个std::Atomic的原子交换<;T*>;以无锁方式在C++;11?,c++,c++11,atomic,atomicity,C++,C++11,Atomic,Atomicity,下面的代码是一个原子指针类的框架,该类取自中的模拟退火应用程序 在该应用程序中,中心数据结构是图形(更具体地说,是集成电路的网表)。图中的每个节点都有一个指示其物理位置的属性。该算法产生许多线程,每个线程重复随机地选择两个节点,并交换它们的物理位置,如果这能为芯片带来更好的路由成本 因为图是巨大的,每个线程都可以选择任意一对节点,所以唯一可行的解决方案是无锁并发数据结构(CDS)。这就是为什么下面的AtomicPtr类非常重要(它用于以无锁方式原子地交换指向两个物理位置对象的指针)。 函数ato

下面的代码是一个原子指针类的框架,该类取自中的模拟退火应用程序

在该应用程序中,中心数据结构是图形(更具体地说,是集成电路的网表)。图中的每个节点都有一个指示其物理位置的属性。该算法产生许多线程,每个线程重复随机地选择两个节点,并交换它们的物理位置,如果这能为芯片带来更好的路由成本

因为图是巨大的,每个线程都可以选择任意一对节点,所以唯一可行的解决方案是无锁并发数据结构(CDS)。这就是为什么下面的
AtomicPtr
类非常重要(它用于以无锁方式原子地交换指向两个物理位置对象的指针)。 函数
atomic\u load\u acq\u ptr()
是在汇编代码中定义的,与
std::atomic::load(memory\u order\u acquire)
密切对应

我想用C++11原子实现这些CD

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
    }
};

std::atomic::load(memory\u order\u acquire)
替换所有
atomic\u load\u acq\u ptr()
调用,用
std::atomic::store(memory\u order\u rel\u ptr)替换所有
调用。但是我的第一个想法是,
std::atomic
应该取代
AtomicPtr
本身,而且可能有一种聪明的方法可以直接交换
std::atomic
对象。有什么想法吗?

您是否签出了CAS(比较和交换)操作

   std::atomic<T*> v;

      while(!v.compare_exchange_weak(old_value,new_value, std::memory_order_release, memory_order_relaxed))
std::atomic v;
而(!v.compare\u exchange\u弱(旧值、新值、标准::内存\u顺序\u释放、内存\u顺序\u松弛))

没有旋转锁,最接近您的是:

std::atomic<T> a;
std::atomic<T> b;
a = b.exchange(a);
std::原子a;
std::原子b;
a=b.交换(a);
对于
b
,它是线程安全的


a
可能无法同时访问。

在我看来,获得所需内容的更简单方法是复制您在此处看到的逻辑

问题是不可能跨两个原子对象执行原子操作,因此必须遵循以下步骤:

  • 订购原子(以避免死锁)
  • “锁定”除最后一个外的所有(递增顺序)
  • 在最后一个上以原子方式执行操作
  • 执行该操作并一次“解锁”另一个(降序)
当然,这是非常不完美的:

  • 非原子:当您忙于锁定变量时,任何尚未锁定的变量都可能会改变状态
  • 非无障碍:如果由于某种原因,某个线程在锁定变量时被阻塞,则所有其他挂起的线程也会被阻塞;注意避免此处出现死锁(如果您有其他锁)
  • 脆性:锁定变量后的崩溃会使您陷入困境,避免可能抛出和/或使用RAII“锁定”的操作
但是,在实践中,如果只有两个对象(因此一个要锁定),它应该工作得比较好

最后,我有两点意见:

  • 为了锁定,您需要能够定义sentinel值,
    0x01
    通常适用于指针
  • C++标准不保证<代码> STD::原子< /代码>是无锁的,您可以用“代码> STD::ActoM::ISHROCKIORE()/<代码> < < /LI>检查您的特定实现和平台。
在C++11中,无法以原子方式交换两个
std::atomic
s的内容。事实上,我认为在x86/x64上甚至不可能实现这一点,但直接交换是不可能的。但是
AtomicPtr
类已经通过遵循签出\签入规程完成了这项工作:1-任何想要进行交换的线程都会首先签出指针(通过编写一个名为
ATOMIC\u NULL
的前哨值),然后在完成后签入。2-如果指针已签出,任何想要读取(即Get())指针值的线程都必须继续旋转。@AhmedNassar:为什么不用
std::atomic
复制此逻辑?@Matthieu:这是我已经提出的唯一解决方案。我一直在寻找更好的解决方案:)但是
std::atomic::compare\u exchange\u-weak()
中的
new\u-value
参数是一个裸露的
T*
而不是
std::atomic
对象。如果先前从
std::atomic
对象中读取了裸
T*
值,则交换将不会是原子的。这不是原子的,因为
std::atomic::exchange()
接受一个裸
T
值,而不是
std::atomic
对象。因此,它将首先调用
a
T
的转换函数,然后将其传递给
b.exchange()
。我从来没有说过
a
是原子的,它只是
b
的原子。正如我在问题中所建议的,移植
AtomicPtr
类以使用C++11原子而不是自定义的汇编级函数是我唯一的解决方案。事实上,
AtomicPtr
类通过在指针持有sentinel值时旋转来构建自己的旋转锁。但非常感谢您详细说明了建议解决方案的缺陷。我使用它作为基准,而不是在生产代码中。顺便说一句,原始代码已经完全按照您的建议定义了sentinel值:
template const T*AtomicPtr::ATOMIC_NULL((T*)((int)NULL+1))
@ahmednasar:出于可移植性的原因,我建议您将
NULL
转换为
intptr\t
,而不是
int
int
在64位代码中通常只有32位。
std::atomic<T> a;
std::atomic<T> b;
a = b.exchange(a);