无锁队列--基于linuxkfifo实现

一直想写个无锁的队列,来提高项目后台的效率。

偶然看到linux内核的kfifo.h 实现原理。于是自己仿照了这个实现,目前linux应该是可以对外提供接口了。


#ifndef _NO_LOCK_QUEUE_H_
#define _NO_LOCK_QUEUE_H_

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <string>
#include <pthread.h>
#include <iostream>
using namespace  std;

#ifndef max
#define max(x, y) ({					typeof(x) _max1 = (x);				typeof(y) _max2 = (y);				(void) (&_max1 == &_max2);			_max1 > _max2 ? _max1 : _max2; })
#endif

#ifndef min
#define min(x, y) ({					typeof(x) _min1 = (x);				typeof(y) _min2 = (y);				(void) (&_min1 == &_min2);			_min1 < _min2 ? _min1 : _min2; })
#endif

class Kfifo 
{
public:
	Kfifo(unsigned int  isize);
	~Kfifo();
	unsigned int get(unsigned char *buffer, unsigned int len);
	unsigned int put(const unsigned char *buffer, unsigned int len);
	static unsigned long roundup_power_of_two(unsigned long val);
private:
	inline bool is_power_of_2(unsigned long n)
	{
		return (n != 0 && ((n & (n - 1)) == 0)); 
	};
	inline unsigned int unused()
	{
		return (mask + 1) - (in - out);
	}
	
private:
	unsigned int size;
	unsigned int in; 
	unsigned int out;
	unsigned int	mask;
	unsigned char* buffer;
};

struct proto
{
	unsigned int msgid;
	unsigned int cmd;
	unsigned int info;
	proto():msgid(0),cmd(0),info(0){}
};

#endif

实现文件

#include "MKfifo.h"

Kfifo::~Kfifo()
{
	if (buffer) free(buffer);	
	size = in = out=0;
}
unsigned long Kfifo::roundup_power_of_two(unsigned long val)
{
	if (val & (val-1) == 0)
	{
		return val;
	}
	unsigned long maxulong = (unsigned long )((unsigned long ) ~0);
	unsigned long andv = ~(maxulong&(maxulong>>1));
	while((andv & val) == 0)
	andv = andv>>1;
	return andv<<1;
}
Kfifo::Kfifo(unsigned int isize):size(isize),in(0),out(0),mask(size - 1)
{
	if (!is_power_of_2(isize))
	{
		size = roundup_power_of_two(isize);
	}
	buffer =(unsigned char*) malloc(isize);
}

unsigned int Kfifo::get(unsigned char *_buffer, unsigned int len)
{
	unsigned int l;
	len = min(len, in - out);
	__sync_synchronize();

	l = min(len,size -(out&(size-1)));
	memcpy(_buffer,buffer + (out& (size-1)),l);
	memcpy(_buffer + l,buffer,len - l);

	 __sync_synchronize();

	 out +=len;
	 return len;

}
unsigned int Kfifo::put(const unsigned char *_buffer, unsigned int len)
{
	unsigned int l;
	len = min(len, size - in + out);
	 __sync_synchronize();
	 l = min(len, size - (in & (size - 1)));
	 memcpy(buffer + (in & (size - 1)), _buffer, l);
	 memcpy(buffer, _buffer + l, len - l);
	  __sync_synchronize();
	   in += len;
	   return len;

}

void * consumer(void * arg)
{
	printf("consumer\n");
	Kfifo* fifo = (Kfifo*) arg;
	if (!fifo)
	{
		return NULL;
	}

	for (;;)
	{
		proto p;
		unsigned int len = fifo->get((unsigned char*)&p,sizeof(p));
		if (len>0)
		{
			cout << "~~~~~~~~~~~~~~~~~~~~"<<endl;
			cout << "consumer proto msg id :"<<p.msgid<<endl;
			cout << "consumer proto msg cmd :"<<p.cmd<<endl;
			cout << "consumer proto msg info :"<<p.info<<endl;
			cout << "~~~~~~~~~~~~~~~~~~~~"<<endl;
		}
	}

	
	return (void *)fifo;
}
void* producer(void* args)
{
	Kfifo* fifo = (Kfifo*) args;
	
	if (!fifo)
	{
		return NULL;
	}
	unsigned int i=0;
	for (;;)
	{
		proto p;
		p.msgid = i++;
		p.cmd =  333;
		p.info = 44444;
		fifo->put((const unsigned char*)&p,sizeof(p));
		cout<<"producer put msgid :"<<p.msgid<<endl;
	}
	return (void*)fifo;
}
int main()
{
	Kfifo *fifo = new Kfifo(1024);
	pthread_t consumeid,producerid;
	pthread_create(&producerid,NULL,producer,(void*)fifo);
	pthread_create(&consumeid,NULL,consumer,(void*)fifo);

	printf("info!!\n");
	pthread_join(consumeid,NULL);
	pthread_join(producerid,NULL);
	return 0;
}


经过测试是可用的,我将在该队列的基础上,丰富其使用。


郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。