spdlog–mpmc_bounded_q.h 无锁队列分析
spdlog中调用了mpmc_bounded_q.h无锁队列实现异步写日志。
构造函数
构造函数传入buffer最大值,并初始化了buffer数组buffer_以及buffer_mask_。
buffer_size必须是2的幂次方。- 设置每个
buffer[i]的值为其序号。 - 设置入队、出队位置为0。
mpmc_bounded_queue(size_t buffer_size)
:max_size_(buffer_size),
buffer_(new cell_t [buffer_size]),
buffer_mask_(buffer_size - 1)
{
//queue size must be power of two
if(!((buffer_size >= 2) && ((buffer_size & (buffer_size - 1)) == 0)))
throw spdlog_ex("async logger queue size must be power of two");
for (size_t i = 0; i != buffer_size; i += 1)
buffer_[i].sequence_.store(i, std::memory_order_relaxed);
enqueue_pos_.store(0, std::memory_order_relaxed);
dequeue_pos_.store(0, std::memory_order_relaxed);
}
入队函数 bool enqueue(T&& data)
- 获取插入的位置
pos = enqueue_pos_ - 获取
pos处的buffer_, 即cell_ - 判断
pos是否等于cell_->sequence_ - 若相等,尝试占领
pos这个位置(enqueue_pos_.compare_exchange_weak),让enqueue_pos_加一,跳出循环 - 若
cell_->sequence_ < pos, 队列中保存的数据已达到max_size_,不入队 - 若
cell_->sequence_ > pos,说明cell_处已经被写入数据,更新pos,重新进入第2步
疑问
cell->sequence_.store(pos + 1, std::memory_order_release);,这里困扰我一阵,为什么要将cell的sequence设为pos+1?
个人见解
我觉得主要作用是标记pos处已经放置数据了。若其他线程获得相同的pos,当其再比较pos和sequence时将不会再相等,就不会再次在相同的pos处写入数据。另外,此处的pos+1和出队时的判断
intptr_t dif = (intptr_t)seq - (intptr_t)(pos + 1);相对应。
bool enqueue(T&& data) // 传入待插入数据的右值引用
{
cell_t* cell;
size_t pos = enqueue_pos_.load(std::memory_order_relaxed); // 获取插入的位置
for (;;) //一直循环,直到enqueue_pos_.compare_exchange_weak返回true
{
cell = &buffer_[pos & buffer_mask_]; // 取出一个buffer_
size_t seq = cell->sequence_.load(std::memory_order_acquire); // 取出buffer_自己的序号
intptr_t dif = (intptr_t)seq - (intptr_t)pos;
if (dif == 0) // 如果是

本文分析了spdlog库中的无锁队列mpmc_bounded_q.h,探讨了构造函数、入队、出队函数的工作原理。入队函数通过原子操作确保线程安全,出队函数则处理队列为空的情况。队列大小函数提供当前队列实际长度。
最低0.47元/天 解锁文章
1573

被折叠的 条评论
为什么被折叠?



