簡體   English   中英

鎖定無界堆棧C ++ 11原子

[英]Lock Free Bounded Stack C++11 atomics

我正在考慮使用非常基本的有界(預分配)堆棧,以正確的LIFO順序跟蹤我的線程ID。 所以我想知道我的實現是否是線程安全的:

// we use maximum 8 workers
size_t idle_ids_stack[8];
// position current write happening at
std::atomic_uint_fast8_t idle_pos(0);

// this function is called by each thread when it is about to sleep
void register_idle(size_t thread_id) 
{
    std::atomic_thread_fence(std::memory_order_release);
    idle_ids_stack[idle_pos.fetch_add(1, std::memory_order_relaxed)] = thread_id;
}

// this function can be called from anywhere at anytime
void wakeup_one() 
{
    uint_fast8_t old_pos(idle_pos.load(std::memory_order_relaxed));
    std::atomic_thread_fence(std::memory_order_acquire);
    size_t id;
    do
    {
        if(old_pos == 0) return; // no idle threads in stack; exit;
        id = idle_ids_stack[old_pos-1];
    }
    while (!idle_pos.compare_exchange_weak(old_pos, old_pos-1, std::memory_order_acquire, std::memory_order_relaxed));
    // wakeup single thread
    signal_thread(id);
}

我不是無鎖編程專家,但是我很確定您的代碼不是線程安全的。

  1. 我們首先來看一下register_idle()

    此處可能發生的事情是idle_pos遞增了idle_pos但是在它存儲其ID之前,另一個線程調用了wakeup_once並使用了過時的ID(在最壞的情況下,甚至是無效的,因為該數組尚未初始化)。 我也看不出內存隔離的原因。

  2. wakeup_one()您有一個類似的問題(稱為ABA問題 ):

    • 您加載當前的idle_pos並根據id加載。
    • 另一個線程調用並完成wakeup_one (idle_pos減少了)。
    • 另一個線程調用register_idle ,它再次將idle_pos增加到與以前相同的值。
    • 現在,第一個線程恢復,認為idle_pos不變,並發出錯誤的線程信號

我可能會弄錯了,但我相信通常無法基於數組創建完全無鎖的堆棧,因為您必須在單個原子操作中做兩件事:修改索引變量並將值存儲或加載到數組中。陣列。

除了這些邏輯錯誤之外,我強烈建議不要使用獨立的內存隔離柵(它們會使代碼的可讀性降低,甚至可能導致成本更高)。 另外,在確保程序正確且默認設置正確之后,我只會開始手動指定內存順序。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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