无锁队列的实现


前言

本文介绍了无锁队列的实现方式


一、无锁队列

我们首先使用一个原子变量实现了无锁队列,随后为了提高并发度,采用三个原子变量进行了实现

1、使用一个原子变量的实现

该实现仅支持同一时刻仅有一个线程对队列进行push或者pop

#include <iostream>
#include <string.h>
#include <atomic>
using namespace std;

const int MAXN = 1010;

class Queue {
public:
    Queue(int len) : maxsize(len + 1), atomic_using(false) {
        memset(que, 0, sizeof(que));
        start = 0;
        end = 0;
    }

    bool EnQueue(int value) {
        // 初始时atomic_using为false, 因此第一个进入do的线程能够返回true跳出循环,之后的会不断循环检查
        bool use_expected = false;
        bool use_desired = true;
        do {
            use_expected = false;
            use_desired = true;
        } while (!atomic_using.compare_exchange_strong(use_expected, use_desired));

        // 队列为满,直接返回
        if ((end + 1) % maxsize == start) {
            do {
                use_expected = true;
                use_desired = false;
            } while (!atomic_using.compare_exchange_strong(use_expected, use_desired));
            return false;
        }

        // 加入元素并更新指针
        que[end] = value;
        end = (end + 1) % maxsize;

        do {
            use_expected = true;
            use_desired = false;
        } while (!atomic_using.compare_exchange_strong(use_expected, use_desired));
        return true;
    }

    bool DeQueue() {
        bool use_expected = false;
        bool use_desired = true;
        do {
            use_expected = false;
            use_desired = true;
        } while (!atomic_using.compare_exchange_strong(use_expected, use_desired));

        // 队列为空,直接返回
        if (start == end) {
            do {
                use_expected = true;
                use_desired = false;
            } while (!atomic_using.compare_exchange_strong(use_expected, use_desired));
            return false;
        }

        start = (start + 1) % maxsize;
        do {
            use_expected = true;
            use_desired = false;
        } while (!atomic_using.compare_exchange_strong(use_expected, use_desired));
        return true;
    }

    int que[MAXN];
    atomic<bool> atomic_using;
    int start, end;
    int maxsize;
};

int main()
{
    Queue *que = new Queue(3);
    cout << que->EnQueue(0) << endl;
    cout << que->EnQueue(1) << endl;
    cout << que->EnQueue(2) << endl;
    cout << que->DeQueue() << endl;
    cout << que->DeQueue() << endl;

    return 0;
}

2、使用三个原子变量

支持多个线程同时对队列进行push与pop操作

#include <iostream>
#include <string.h>
#include <atomic>
using namespace std;

const int MAXN = 1010;

/*
前一个版本只用了一个原子变量,虽然可以保证线程安全,但是
push操作和pop操作的耦合性过高,同一时刻只能有一个线程pop或者push;
事实上,一个线程在push的时候,可能并不会影响另一个线程pop,为了提高并发
,引入两个变量使得能够同时进行push和pop操作;
但是,当一个线程push时,第一步需要移动end指针,第二步才将元素放入队列中,
可能带来的隐患就是一个线程执行完第一步之后,另一个线程开始pop,造成pop出来的元素
与应当push(实际还未push)的不一致。因此,需要引入第三个变量保证其一致性
*/

class Queue {
public:
    Queue(int len) : maxsize(len + 1) {
        memset(que, 0, sizeof(que));
        start = 0;
        end = 0;
        last = 0;
    }

    bool EnQueue(int value) {
        size_t e;
        do {
            // 取出队首指针
            e = end.load();
            // 判断队列是否为满
            if ((e + 1) % maxsize == start.load()) {
                return false;
            }
        } while (!end.compare_exchange_strong(e, (e + 1) % maxsize));

        // 元素入队
        que[e] = value;

        /*
        第二个cas操作保证赋值顺序与指针移动顺序一致,只有当先前移动过指针的线程
        将元素放入队列中后,后面的线程才能继续将元素放入后面的空间
        */
        size_t l;
        do {
            l = e;
        } while (last.compare_exchange_strong(l, (l + 1) % maxsize));

        return true;
    }

    bool DeQueue() {
        size_t s;
        do {
            s = start.load();
            // 队列为空,直接返回
            if (s == end.load()) {
                return false;
            }

            // 还需要判断,push线程是否真的把元素写入到队列了,如果仅仅只是移动了指针,那么就不能pop
            if (s == last.load()) {
                return false;
            }

            // 此处可以读取数据
        } while (!start.compare_exchange_strong(s, (s + 1) % maxsize));
        return true;
    }

    int que[MAXN];
    atomic<size_t> start, end, last;
    int maxsize;
};

int main()
{
    Queue *que = new Queue(3);
    cout << que->EnQueue(0) << endl;
    cout << que->EnQueue(1) << endl;
    cout << que->EnQueue(2) << endl;
    cout << que->DeQueue() << endl;
    cout << que->DeQueue() << endl;

    return 0;
}

参考文章

1、无锁并发队列

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值