无锁“减量如果不是零”(Lock-free “decrement if not zero”)

2019-09-26 19:04发布

目前,我正在重塑C ++中的线程池的车轮。 我已经消除了从代码几乎所有的锁,除了下面的结构的多个实例:

std::atomic_size_t counter;

void produce() {
    ++counter;
}

void try_consume() {
    if (counter != 0) {
        --counter;
        // ...
    }
    else {
        // ...
    }
}

所以,我需要这个功能的线程安全的无锁版:

bool take(std::atomic_size_t& value) {
    if (value != 0) {
        --value;
        return true;
    }
    return false;
}

有一个解决方案,我所知道的:使用boost::lockfree::queue<std::monostate>其中pop()做这项工作。 有没有更好/更快的解决方案?

Answer 1:

你实现结构是一个计数的锁,或计数信号 。 使用一个从具有一个图书馆trylock版本,而不是滚动您自己,这样你可以得到最优化的操作系统支持的睡眠/唤醒。 或者你总是有益的工作要做,如果trylock (又名take )失败?

请注意,您可避免使用任何传统的锁,同时实现自己的,而是“无锁”是一个技术术语有不同的含义比无锁。 消费者几乎可以肯定不可能是无锁的计算机科学意义,因为它可以被卡住,如果生产者线程(S)被阻塞等下去。 相关: 无锁进展担保


CAS是好的。 只要确保你的函数不运行compare_exchange_weak在所有如果它看到柜台已经0用纯负荷(通常memory_order_relaxed )。 你不希望你的CPU锤击的位置,而其他线程正在试图增加它使你的线程会看到一个非零值。

另一种选择是一个签名计数器,并改变比较>= 0 。 检查过冲的结果fetch_add(-1)如果是正确的。 (即看到柜台作为暂时的负面线程只看到它“锁定”)。 但是,这通常是不超过CAS重试循环更好; 失败的CAS是罕见的,除非争是非常高的。 和额外的原子OPS纠正超调可能会花费一样多(或更多)CAS重试。



文章来源: Lock-free “decrement if not zero”