簡體   English   中英

原子交換兩個std :: atomic <T*> 在C ++ 11中以無鎖方式對象?

[英]Atomic exchange of two std::atomic<T*> objects in a lock-free manner in C++11?

以下代碼是一個原子指針類的框架,它取自PARSEC基准套件中用於共享內存多處理器的模擬退火應用程序。

在該應用中,中央數據結構是圖(更具體地,集成電路的網表)。 圖中的每個節點都有一個指示其物理位置的屬性。 該算法產生許多線程並且每個線程重復並隨機選擇兩個節點並交換它們的物理位置,如果這導致芯片的更好的路由成本。

因為圖形很大並且每個線程都可以選擇任何一對節點,所以唯一可行的解​​決方案是無鎖並發數據結構(CDS)。 這就是為什么以下AtomicPtr類是至關重要的(它用於以無鎖方式原子地交換指向兩個物理位置對象的指針)。 函數atomic_load_acq_ptr()是在匯編代碼中定義的,並且與std::atomic<T*>::load(memory_order_acquire)緊密對應。

我想用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
    }
};

std::atomic<T*>::exchange()方法只能用於與std::atomic<T*>對象交換裸T*指針。 如何以無鎖方式交換兩個std::atomic<T*>對象?

我能想到的是下面的AtomicPtr類本身可以基於std::atomic<T*>來聲明:

std::atomic<T*> p;

和更換所有atomic_load_acq_ptr()通過調用std::atomic<T*>::load(memory_order_acquire)和替換所有atomic_store_rel_ptr()通過調用std::atomic<T*>::store(memory_order_release) 但我的第一個想法是std::atomic<T*>應該取代AtomicPtr本身,並且可能有一種聰明的方式直接交換std::atomic<T*>對象。 有什么想法嗎?

在我看來,獲得你想要的更簡單的方法是復制你在這里看到的邏輯。

問題是不可能跨兩個原子對象獲取原子操作,因此您必須遵循以下過程:

  • 命令原子(避免死鎖)
  • “鎖定”除了最后一個(增加順序)
  • 在最后一個上以原子方式執行操作
  • 執行操作並一次“解鎖”其他一個(遞減順序)

當然,這非常不完美:

  • 不是原子的:當你忙着鎖定變量時,任何尚未鎖定的變量都可以改變狀態
  • 不受阻礙:如果由於某種原因在鎖定變量時線程被阻塞,則所有其他未決線程也被阻止; 小心避免死鎖(如果你有其他鎖)
  • 脆弱:鎖定變量后崩潰使您陷入困境,避免可能拋出和/或使用RAII“鎖定”的操作

然而,在僅有2個物體(因此鎖定一個物體)的情況下,它在實踐中應該相對較好地工作。

最后,我有兩個評論:

  • 為了鎖定你需要能夠定義一個sentinel值, 0x01通常適用於指針。
  • C ++標准不保證std::atomic<T*>是無鎖的,你可以使用std::atomic<T*>::is_lock_free()來檢查你的特定實現和平台。

沒有螺旋鎖的最接近的是:

std::atomic<T> a;
std::atomic<T> b;
a = b.exchange(a);

哪個是b線程安全。

a可能無法同時訪問。

你檢查了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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM