无锁环形缓冲区

2016-11-14  本文已影响0人  yuansip

对于单生产者-单消费者的情况,可以实现无锁环形缓冲区,常见的实现如下:

public class RingBuffer {
    private int mHead;
    private int mTail;
    private char[] mBuffer;
    public RingBuffer(int size) {
        if (size < 2) {
            throw new Exception("size should be greater than 2");
        }
        mBuffer = new char[size];
        mHead = 0;
        mTail = 0;
    }
    public int length() {
        return (mTail + mBuffer.length - mHead) % mBuffer.length;
    }
    public boolean isEmpty() {
        return mHead == mTail;
    }
    public boolean isFull() {
        return mHead == ((mTail + 1) % mBuffer.length);
    }
    public char read() {
        if (isEmpty()) {
            throw new Exception("buffer is already empty");
        }
        char ch = mBuffer[mHead];
        mHead = (mHead + 1) % mBuffer.length;
        return ch;
    }
    public void write(char ch) {
        if (isFull()) {
            throw new Exception("buffer is already full");
        }
        mBuffer[mTail] = ch;
        mTail = (mTail + 1) % mBuffer.length;
    }
}

mHead表明下一个可读取的位置,mTail表明下一个可写入的位置,head和tail重合时,表明buffer是空的;为了区分empty和full,强制要求tail和head之间至少要空一个元素,也就是说对于size为N的数组实现的环形buffer,最多只能存放N - 1个元素。如果不做这样的强制要求,则无法区分empty和full。因此,数组的size要大于2才有效。read和write函数各自只修改了head和tail, 所以即使运行在不同的线程,也能保证数据的一致性。

对于size为N的数组实现的环形buffer,如果想实现最多可以存放N个元素,则必须引入length变量,而且无法做到lock-free。也就是说,只借助head和tail, 无法区分empty和full, 所以要借助empty和full时head和tail的间隔不同来帮忙区分。

public class RingBuffer {
    private int mHead;
    private int mTail;
        private int mLength;
    private char[] mBuffer;
    public RingBuffer(int size) {
        if (size < 1) {
            throw new Exception("size should be greater than 0");
        }
        mBuffer = new char[size];
        mHead = 0;
        mTail = 0;
    }
    public int length() {
        return mLength;
    }
    public boolean isEmpty() {
        return length() == 0;
    }
    public boolean isFull() {
        return length() == mBuffer.length;
    }
    public char read() {
        if (isEmpty()) {
            throw new Exception("buffer is already empty");
        }
        char ch = mBuffer[mHead];
        mHead = (mHead + 1) % mBuffer.length;
                mLength--;
        return ch;
    }
    public void write(char ch) {
        if (isFull()) {
            throw new Exception("buffer is already full");
        }
        mBuffer[mTail] = ch;
                mTail = (mTail + 1) % mBuffer.length;
        mLength++;
    }
}

参考阅读

深入浅出网络模型
透过linux内核看无锁编程

上一篇 下一篇

猜你喜欢

热点阅读