我目前正在重新发明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()可以完成工作。有没有更好/更快的解决方案?

最佳答案

您要实现的构造是计数锁定或counting semaphore。使用具有trylock版本的库中的库来代替自己的库,这样就可以优化操作系统支持的睡眠/唤醒功能。还是如果trylock(aka take)失败了,您是否总是有工作要做?

请注意,在实现自己的锁时可以避免使用任何传统锁,但是“无锁”是一个技术术语,其含义与无锁不同。从计算机科学的 Angular 来看,从定义上讲,消费者几乎不可能是lock-free,因为如果生产者线程被阻塞,它可能会一直处于等待状态。相关:Lock-free Progress Guarantees

CAS很好。只要确保函数在纯负载(通常是compare_exchange_weak)下看到计数器已经为0的情况下,就根本不运行memory_order_relaxed即可。您不希望您的CPU在其他线程试图递增该位置时对其进行锤击,因此您的线程将看到非零值。

另一个选项是签名计数器,然后将比较更改为>= 0。检查fetch_add(-1)的结果是否过冲,如果正确,请更正。 (将计数器视为暂时为负的线程只会将其“锁定”)。但这通常不比CAS重试循环更好。除非争端很高,否则失败的CAS很少见。额外的原子操作来纠正过冲可能比CAS重试花费大约(或更多)的费用。

关于c++ - 无锁 “decrement if not zero”,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/47650290/

10-11 16:25