我的记录器在SPSC队列下面。
它肯定不是一个通用的SPSC无锁队列。
然而,考虑到关于它将如何使用、目标体系结构等的一系列假设,以及一些可以接受的折衷(我将在下面详细介绍这些权衡),我的问题基本上是,它是否安全/是否有效?
x86_64体系结构,因此对uint16_t的写入将是原子化的。tail。head。head的旧值,那么队列中的空间将比实际少,这在所使用的上下文中是一个可接受的限制。tail的旧值,那么队列中等待的数据似乎比实际少,这也是一个可以接受的限制。上述限制是可以接受的,因为:
tail,但最终将到达最新的tail,并将记录排队的数据。head,所以队列看起来比实际的更满。在我们的负载测试中,我们已经发现了我们记录的数量与队列的大小,以及记录器排出队列的速度,这个限制没有影响-队列中总是有空间。最后一点,必须使用volatile来防止每个线程只读取的变量被优化。
我的问题:
volatile足够了吗?volatile有必要吗?My队列:
class LogBuffer
{
public:
bool is_empty() const { return head_ == tail_; }
bool is_full() const { return uint16_t(tail_ + 1) == head_; }
LogLine& head() { return log_buffer_[head_]; }
LogLine& tail() { return log_buffer_[tail_]; }
void advance_head() { ++head_; }
void advance_hail() { ++tail_; }
private:
volatile uint16_t tail_ = 0; // write position
LogLine log_buffer_[0xffff + 1]; // relies on the uint16_t overflowing
volatile uint16_t head_ = 0; // read position
};发布于 2014-11-26 00:37:14
这个逻辑正确吗?
是。
队列线程安全吗?
不是的。
挥发性足够了吗?不稳定是必要的吗?
不对两个人来说。易失性不是使任何变量线程安全的神奇关键字。您仍然需要为索引使用原子变量或内存屏障,以确保在生成或使用项时内存排序是正确的。
具体来说,在您为队列生成或使用一个项之后,您需要发出一个内存屏障,以保证其他线程将看到这些更改。当您更新原子变量时,许多原子库将为您完成此操作。
顺便说一句,使用"was_empty“而不是"is_empty”来明确它的功能。此调用的结果是时间上的一个实例,它可能在您对其值进行操作时发生了更改。
https://stackoverflow.com/questions/27139260
复制相似问题