无锁循环队列的应用

前言

为充分利用向量空间,克服"假溢出"现象的方法是:将向量空间想象为一个首尾相接的圆环,并称这种向量为循环向量。存储在其中的队列称为循环队列(Circular Queue)。这种循环队列可以以单链表的方式来在实际编程应用中来实现。

正文

一, 无锁循环队列的条件处理

循环队列中,由于入队时尾指针向前追赶头指针;出队时头指针向前追赶尾指针,造成队空和队满时头尾指针均相等。
因此,无法通过条件m_head == m_tail来判别队列是"空"还是"满"。

解决这个问题的方法至少有两种:

1, 另设一布尔变量以区别队列的空和满;
2, 另一种方式就是数据结构常用的: 队满时:(m_tail+1) % m_maxSize == m_head,m_maxSize为队列长度(所用数组大小),由于m_tail,m_head均为所用空间的指针,循环只是逻辑上的循环,所以需要求余运算。

当队列的大小为 m_numsize = 6;

“队已满”, 但是m_tail(5)+1=6!= m_head(0),对空间长度求余,作用就在此6%6=0=m_head(0)。

"队列满"时 当 index == (m_maxSize) 到最大时就要 下标重置到0

类型定义采用环状模型来实现队列,各数据成员的意义如下:

  1. m_head指定队首位置,删除一个元素就将m_head顺时针移动一位;
  2. m_tail指向元素要插入的位置,插入一个元素就将m_tail顺时针移动一位;
  3. m_size存放队列中元素的个数,当m_size等于m_maxSize时,不可再向队列中插入元素。

m_size=0

m_size = m_maxSize

T*	m_datas;
int	m_maxSize;  应根据具体情况定义该值队列的大小
volatile int    m_size;  计数器,记录队中元素总数
volatile int	m_head; 头指针,队非空时指向队头元素
volatile int	m_tail; 尾指针,队非空时指向队尾元素的下一位置

实现

template<typename T>
	class cycle_queue
	{
		//-- constructor/destructor
	public:
		cycle_queue();
		~cycle_queue();

		//-- member function
	public:
		//-- prop
		bool init(int uMaxNum);
		void destroy();

		bool empty() const { return (0 == m_size); }
		bool full() const { return (m_size == m_maxSize); }
		int	size() const { return m_size; }
		int	get_max_size() const { return m_maxSize; }

		bool push(const T& data);
		bool pop(T& data);
		void clear();

	private:
		cycle_queue(const wcycle_queue&);
		cycle_queue& operator=(const wcycle_queue&);

	private:
		T*	            m_datas;
		int	            m_maxSize;
		volatile int    m_size;
		volatile int	m_head;
		volatile int	m_tail;
	};

	template<typename T>
	cycle_queue<T>::cycle_queue() : m_datas(NULL), m_maxSize(0), m_size(0), m_head(0), m_tail(0)
	{

	}

	template<typename T>
	cycle_queue<T>::~cycle_queue()
	{
		destroy();
	}

	template<typename T>
	void cycle_queue<T>::destroy()
	{
		if (m_datas)
		{
			delete[] m_datas;
			m_datas = NULL;
		}
		m_maxSize = 0;
		m_head = 0;
		m_tail = 0;
	}

	template<typename T>
	bool cycle_queue<T>::init(int uMaxNum)
	{
		m_datas = new T[uMaxNum];
		if (!m_datas)
		{
			return false;
		}
		m_maxSize = uMaxNum;
		return true;
	}
	
	template<typename T>
	bool cycle_queue<T>::push(const T& data)
	{
		if (!full())
		{
			m_datas[m_tail] = data;
			m_tail = (m_tail + 1) % m_maxSize;
			++m_size;
			return true;
		}
		return false;
	}

	template<typename T>
	bool cycle_queue<T>::pop(T& data)
	{
		if (!empty())
		{
			data = m_datas[m_head];
			m_head = (m_head + 1) % m_maxSize;
			--m_size;
			return true;
		}
		return false;
	}

	template<typename T>
	void cycle_queue<T>::clear()
	{
		m_size = 0;
		m_head = 0;
		m_tail = 0;
	}

二, volatile 关键字的使用

int b=i;

volatile 指出 i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的汇编代码会重新从i的地址读取数据放在b中。而优化做法是,由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在b中。而不是重新从i里面读。这样一来,如果i是一个寄存器变量或者表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的稳定访问。

结语

无锁循环队列的应用中有一个重要的算法, 就是判断是否到了最后一个下标的修改下标到第一个

m_head  = (m_head + 1) % m_maxsize;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值