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

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

  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的比较结果没有注意函数结果,导致出错

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值