一次无锁循环队列的编写回顾

  今天写了一个无锁队列,代码比较短,但是调试时间比较久,主要的几个问题是:
  

  1. 函数用法不清楚(CAS)
  2. 函数起名不准确,导致用错
  3. 算法不熟练,导致调试时发现算法的偏移又问题
    代码如下:
#ifndef LYY_LIB_LOCK_FREE_QUEUE_H
#define LYY_LIB_LOCK_FREE_QUEUE_H
#include <vector>
#include <cstdint>
using std::vector;
namespace lyy {
    #define CAS __sync_bool_compare_and_swap
    enum NodeStatus {
        STATUS_IDLE,
        STATUS_ADDING,
        STATUS_READY,
        STATUS_REMOVING,
    };
    struct Node {
        void *ptr;
        int status;
    };
    class Queue {
        public:
            Queue() : _max_queue_size(100) {
            }
            Queue(int max_queue_size) : _max_queue_size(max_queue_size) {
            }
            virtual void * get() = 0;
            virtual void put(void* t) = 0;
        protected:
            int _max_queue_size;
    };
    class LockFreeQueue : public Queue {
        public:
            LockFreeQueue() : _head_tail(0) {
                _data.resize(_max_queue_size);
            }
            LockFreeQueue(int max_queue_size) : Queue(max_queue_size), _head_tail(0) {
                _data.resize(_max_queue_size);
            }
            void * get();
            void put(void* t);

            int empty();
            int full();
        private:
            bool add_tail(uint64_t &);
            bool add_head(uint64_t &);
        private:
            vector<Node> _data;
            uint64_t _head_tail;
    };
}
#endif
cpp:
#include "queue.h"
#include <pthread.h>
#include <unistd.h>
namespace lyy {

bool LockFreeQueue::add_tail(uint64_t & new_head_tail) {
    uint32_t cur_size = 0;
    uint32_t tail = _head_tail & 0xFFFFFFFF;
    uint32_t head = (uint32_t)(_head_tail >> 32);
    if (tail >= head) {
        cur_size = tail -head;
    } else {
        cur_size = tail + _max_queue_size - head;
    }
    if (cur_size == _max_queue_size - 1) {
        return false;
    }
    tail = (tail + 1) % (_max_queue_size);
    new_head_tail = (((uint64_t)head)<<32) | tail;
    return true;
}

bool LockFreeQueue::add_head(uint64_t & new_head_tail) {
    uint32_t cur_size = 0;
    uint32_t tail = _head_tail & 0xFFFFFFFF;
    uint32_t head = (uint32_t)(_head_tail >> 32);
    if (tail >= head) {
        cur_size = tail -head;
    } else {
        cur_size = tail + _max_queue_size - head;
    }
    if (cur_size == 0) {
        return false;
    }
    head = (head + 1) % _max_queue_size;
    new_head_tail = (((uint64_t)head)<<32) | tail;
    return true;
}


void *LockFreeQueue::get() {
    uint64_t new_head_tail = 0;
    uint64_t old_head_tail = _head_tail;
    int retry = 3;
    bool suc = true;
    do {
        do {
            old_head_tail = _head_tail;
            if ((suc = add_head(new_head_tail))) {
            } else {
                if (retry--) {
                    usleep(5);
                } else {
                    pthread_yield();
                }
            }
        } while (!suc);
        retry = 3;
    } while (!CAS(&_head_tail, old_head_tail, new_head_tail));
    uint32_t head = ((new_head_tail >> 32)  + _max_queue_size - 1) % _max_queue_size;
    Node * node = &_data[head];         
    do {
    } while (!CAS(&node->status, STATUS_READY,STATUS_REMOVING));
    void *ptr= node->ptr;
    node->ptr = NULL;
    node->status = STATUS_IDLE;
    return ptr;
}

void LockFreeQueue::put(void *ptr) {
    uint64_t new_head_tail = 0;
    uint64_t old_head_tail = _head_tail;
    int retry = 3;
    bool suc = false;
    do {
        do {
            old_head_tail = _head_tail;
            if (suc = add_tail(new_head_tail)) {
            } else {
                if (retry--) {
                    usleep(5);
                } else {
                    pthread_yield();
                }
            }
        } while (!suc);
        retry = 3;
    } while (!CAS(&_head_tail, old_head_tail, new_head_tail));
    uint32_t tail = new_head_tail & 0xFFFFFFFF;
    Node * node = &_data[(tail + _max_queue_size - 1) % _max_queue_size];         
    do {
    } while (!CAS(&node->status, STATUS_IDLE, STATUS_ADDING));
    node->ptr = ptr;
    node->status = STATUS_READY;
}
}

  测试代码:
  

#include "queue.h"
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <cstdio>

struct Context {
    lyy::LockFreeQueue *q;
};
struct Data {
    pthread_t tid;
    int value;
    void print() {
        std::ostringstream oss;
        oss << "tid:" << tid << " value:" << value << std::endl;
        printf("%s",oss.str().c_str());
    }
};
void* put(void *ptr) {
    Context *context = (Context *)ptr;
    pthread_t tid = pthread_self();
    std::ostringstream oss;
    oss << "---------put start[tid:" <<tid << "]-------------\n";
    printf("%s", oss.str().c_str());
    for (int i = 0; i < 5; ++ i) {
        Data *data = (Data*)malloc(sizeof(Data));
        data->tid = tid;
        data->value = i;
        context->q->put(data);
    }
}
void *get(void *ptr) {
    Context *context = (Context *)ptr;

    pthread_t tid = pthread_self();
    std::ostringstream oss;
    oss << "---------get start[tid:" <<tid << "]-------------\n";
    printf("%s", oss.str().c_str());
    for (int i = 0; i < 5; ++i) {
       Data *data = (Data*) context->q->get();
       if (data != NULL) {
            data->print();
       }
    }
}

int main() {
    lyy::LockFreeQueue queue;
    Context context;
    context.q = &queue;

    int get_thread_num = 100;
    int put_thread_num = 100;
    std::vector<pthread_t> get_pthread_ts(get_thread_num, 0);
    std::vector<pthread_t> put_pthread_ts(put_thread_num, 0);
    for (int i = 0; i < get_thread_num; ++i) {
        pthread_create(&get_pthread_ts[i], NULL, get, &context);
        pthread_create(&put_pthread_ts[i], NULL, put, &context);
    }

    for (int i = 0; i < get_thread_num; ++i) {
        pthread_join(get_pthread_ts[i], NULL);
        pthread_join(put_pthread_ts[i], NULL);
    }
}

  add_tail&add_head 的名字问题:add_head和add_tail名字表达不准确,add_head应该叫remove_head
  CAS的比较结果没有注意函数结果,导致出错

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是用 C++ 编写循环队列示例代码: ```c++ #include <iostream> using namespace std; class CircularQueue { private: int *queue; // 队列数组指针 int front; // 队首索引 int rear; // 队尾索引 int size; // 队列大小 public: CircularQueue(int k) { // 构造函数,初始化队列 queue = new int[k]; front = 0; rear = -1; size = k; } bool enqueue(int value) { // 入队操作 if (isFull()) { // 队列已满,入队失败 return false; } rear = (rear + 1) % size; queue[rear] = value; return true; } bool dequeue() { // 出队操作 if (isEmpty()) { // 队列为空,出队失败 return false; } front = (front + 1) % size; return true; } int frontValue() { // 获取队首元素 if (isEmpty()) { return -1; } return queue[front]; } int rearValue() { // 获取队尾元素 if (isEmpty()) { return -1; } return queue[rear]; } bool isEmpty() { // 判断队列是否为空 return front == (rear + 1) % size; } bool isFull() { // 判断队列是否已满 return front == (rear + 2) % size; } }; int main() { CircularQueue q(5); // 创建大小为 5 的循环队列 q.enqueue(1); // 入队 1 q.enqueue(2); // 入队 2 q.enqueue(3); // 入队 3 q.enqueue(4); // 入队 4 q.enqueue(5); // 入队 5,此时队列已满,入队失败 cout << q.frontValue() << endl; // 输出队首元素,即 1 q.dequeue(); // 出队,队列中元素变为 2,3,4,5 cout << q.frontValue() << endl; // 输出队首元素,即 2 cout << q.rearValue() << endl; // 输出队尾元素,即 5 return 0; } ``` 这个循环队列的实现采用了“队首指针指向队列第一个元素的前一个位置,队尾指针指向队列最后一个元素”的方式,这样可以方便地判断队列是否为空或已满。在入队操作中,先将队尾指针加一,然后将元素插入到队尾指向的位置;在出队操作中,先将队首指针加一,然后将队首指向的元素删除。同时,队列大小为 k 时,队列中最多可以存储 k-1 个元素,因此在判断队列是否已满时需要将队首指针减一后再与队尾指针比较。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值