Brpc源码浅析(四)WorkStealingQueue类实现

源码路径

work_stealing_queue.h

原理简析

申请一块连续内存数组作为队列,本地工作线程往队列尾部push和pop队列数据,其余竞争线程往队列首部steal队列数据,一首一尾,可以减少竞争。队列使用lock free技术实现

实现

类的数据成员:

    butil::atomic<size_t> _bottom; //指向队列的尾部
    size_t _capacity;//队列容量总大小
    T* _buffer;//指向分配给队列的堆内存首地址
    butil::atomic<size_t> BAIDU_CACHELINE_ALIGNMENT _top;//指向队列的头部

构造和初始化:
构造时将bottom和top变量都指向数组小标为【1】的位置,初始化时要让队列的初始化大小为2的整数倍。这两个的目的是:快速定位到访问元素并且支持循环访问。

push函数

    // Push an item into the queue.
    // Returns true on pushed.
    // May run in parallel with steal().
    // Never run in parallel with pop() or another push().
    bool push(const T& x) {
        const size_t b = _bottom.load(butil::memory_order_relaxed);
        //steal会修改top值,这里用acquire是为了同步steal线程修改top值之前的修改。这里其实也可以不用acquire的,因为并没有其他需要同步的数据
        const size_t t = _top.load(butil::memory_order_acquire);
        if (b >= t + _capacity) { // Full queue.
            return false;
        }
        //在同一时刻整个队列只有一个线程在做push操作,如果前面判断还有空间,那就一定还有空间了
        _buffer[b & (_capacity - 1)] = x; //包含了一个取余操作,第一个push()调用是放在下标1那里
        //这里用了memory_order_release保证别人在steal的时候如果看到了最新的bottom值,就能看到实际buffer里面的内容了
        //bottom值只有push/pop自己这个线程会修改,其他线程steal时都只是读而已
        _bottom.store(b + 1, butil::memory_order_release);
        return true;
    }

先获取bottom和top的值,如果队列已满则return false。否则这个队列就一定有空闲块,因为只有本线程会往队列里面塞元素。

pop函数

    // Pop an item from the queue.
    // Returns true on popped and the item is written to `val'.
    // May run in parallel with steal().
    // Never run in parallel with push() or another pop().
    bool pop(T* val) {
        const size_t b = _bottom.load(butil::memory_order_relaxed);
        size_t t = _top.load(butil::memory_order_relaxed);
        //t跟b指向同一个位置,此时是没有元素的,本线程也没有在push数据进去,所以肯定是没有元素了
        if (t >= b) {
            // fast check since we call pop() in each sched.
            // Stale _top which is smaller should not enter this branch.
            return false;
        }
        //newb就是我要pop的元素了,从底部开始
        const size_t newb = b - 1; 
        //当我store之后,最多只有一个steal能看到我修改前的bottom值并返回true,其他都得回到循环来读取我这个store的新值
        _bottom.store(newb, butil::memory_order_relaxed);//已经占用了一个位置,后来的steal会失败
        butil::atomic_thread_fence(butil::memory_order_seq_cst);//保证t赋值完成后bottom的store已经全局可见了,怎么保证,因为他也阻止了storeload的重排
        t = _top.load(butil::memory_order_relaxed);
        //bottom在占用位置之前已经被一个steal占用了,return false(那个唯一的steal已经执行好了compare_exchange_strong抢了,修改了top的值)
        if (t > newb) {
            _bottom.store(b, butil::memory_order_relaxed);
            return false;
        }
        *val = _buffer[newb & (_capacity - 1)];
        //若相等则存在单个元素竞争的情况,若不等则至少有两个元素,最多又只有一个steal,所以肯定可以return true
        if (t != newb) {
            return true;
        }
        // Single last element, compete with steal()
        //如果我拿到了这个元素,我把top后移一位而不是bottom前移一位
        //如果我没有拿到这个元素,则不需要做什么,bottom还是要保留原来的值
        const bool popped = _top.compare_exchange_strong(
            t, t + 1, butil::memory_order_seq_cst, butil::memory_order_relaxed);
        _bottom.store(b, butil::memory_order_relaxed);
        return popped;
    }

steal函数

    // Steal one item from the queue.
    // Returns true on stolen.
    // May run in parallel with push() pop() or another steal().
    bool steal(T* val) {
        //acquire读bottom确保push写入的buffer元素的可见性
        size_t t = _top.load(butil::memory_order_acquire);
        size_t b = _bottom.load(butil::memory_order_acquire);
        //t>=b的时候队列可能并不空,因为bottom 和top这种共享变量都是有同步延迟的。
        if (t >= b) {
            // Permit false negative for performance considerations.
            return false;
        }
        //无锁队列?
        do {
            butil::atomic_thread_fence(butil::memory_order_seq_cst);//保证读bottom的值的时候top的值已经全局可见了?
            b = _bottom.load(butil::memory_order_acquire);//bottom要拿到实际buffer的值所以都要用acquire
            if (t >= b) {
                return false;
            }
            *val = _buffer[t & (_capacity - 1)];
        } while (!_top.compare_exchange_strong(t, t + 1,
                                               butil::memory_order_seq_cst,//成功的内存序
                                               butil::memory_order_relaxed));//失败的内存序
        return true;
    }

一个相关的issue:work_stealing_queue的原子屏障疑问?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值