C++ 无锁有界堆栈C++;11原子

C++ 无锁有界堆栈C++;11原子,c++,multithreading,c++11,stack,lock-free,C++,Multithreading,C++11,Stack,Lock Free,我在考虑使用非常基本的有界(预分配)堆栈,以正确的后进先出顺序跟踪线程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 t

我在考虑使用非常基本的有界(预分配)堆栈,以正确的后进先出顺序跟踪线程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);
}

我不是无锁编程方面的专家,但我非常确定您的代码不是线程安全的

  • 让我们首先看一下
    寄存器\u idle()

    这里可能发生的情况是,Thread1递增
    idle\u pos
    ,但在它存储其id之前,另一个线程调用
    wakeup\u一次
    ,并使用过期的id(在最坏的情况下,甚至是无效的on,因为数组尚未初始化)。我也不明白为什么会有记忆障碍

  • wakeup_one()
    中,您会遇到类似的问题(称为:

    • 您加载当前的
      空闲位置
      并根据
      id
    • 另一个线程调用并完成唤醒一个线程(空闲位置减少)
    • 还有一个线程调用寄存器空闲,这会将空闲位置再次增加到与以前相同的值
    • 现在第一个线程继续运行,认为空闲线程没有改变,并发出错误线程的信号
  • 我可能弄错了,但我相信通常不可能基于数组创建完全无锁堆栈,因为在单个原子操作中必须做两件事:修改索引变量并在数组中存储或加载值


    除了这些逻辑错误之外,我强烈建议不要使用独立内存围栏(它们会降低代码的可读性,甚至可能会增加成本)。此外,我只会在确保程序与默认顺序正确后才开始手动指定内存顺序。

    这可能更适合代码审查……您应该很少使用fences。直接同步原子变量。您可能需要使用一些测试工具,例如: