今天写了一个无锁队列,代码比较短,但是调试时间比较久,主要的几个问题是:
- 函数用法不清楚(CAS)
- 函数起名不准确,导致用错
- 算法不熟练,导致调试时发现算法的偏移又问题
代码如下:
#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的比较结果没有注意函数结果,导致出错