[数据结构]——无锁队列_无锁队列快多少

}

~LockFreeQueue()
{
    Node\* cur = _head;
    while(cur)
    {
        Node\* next = cur->_next;
        delete cur;
        cur = next;
    }
}

private:
atomic<Node*> _head;
atomic<Node*> _tail;
};



> 
> 无锁队列如何入队列?
> 
> 
> 


![在这里插入图片描述](https://img-blog.csdnimg.cn/20190922165413539.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2x1Y2t5NTI1Mjk=,size_16,color_FFFFFF,t_70)  
 下面介绍一下atomic类型的两个函数操作



//取出当前atomic对象指向的值
T load (memory_order sync = memory_order_seq_cst) const volatile noexcept;
//如果内存中的值和参数一相同,那么替换为参数二,成功返回true
bool compare_exchange_weak (T& expected, T val,
memory_order sync = memory_order_seq_cst) volatile noexcept;


两个函数看起来很复杂,用起来其实超级简单,现在一起来看对无锁队列的插入代码块:



void Enqueue(const T& x)
{
Node* newnode = new Node(x);//要插入的新值
Node* oldtail = nullptr;//旧的尾节点
Node* nullnode = nullptr;//空节点
do
{
oldtail = _tail.load();//用load取出当前_tail节点的值
}
//如果当前tail节点的下一个节点是空,也就是等于参数一,那么修改为参数二
while (oldtail->_next.compare_exchange_weak(nullnode, newnode) != true);
//由于现在的真正的尾节点是newnode,所以将_tail节点更新为newnode
_tail.compare_exchange_weak(oldtail, newnode);
}


这里要明白,while条件里的CAS语句是为了插入这个新节点,而最后一句是为了将控制整个队列的\_tail指针修改为指向新尾部



> 
> 无锁队列如何出队列?
> 
> 
> 


![在这里插入图片描述](https://img-blog.csdnimg.cn/20190922170932644.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2x1Y2t5NTI1Mjk=,size_16,color_FFFFFF,t_70)  
 出队列比插入更简单,那么废话不多说,直接看看出队列如何实现:



T DeQueue()
{
Node* oldhead = _head.load();//取出当前的头节点,也就是哑节点
T ret;
do
{
Node* next = oldhead->_next;//取出头节点的下一个节点,此节点中有我们想要的值
if(next == nullptr)
{
return T();
}
else
{
ret = next->_value;//取走值
}
}
//将头节点(哑节点)修改为下一个节点
while(_head.compare_exchange_weak(oldhead,oldhead->_next) != true);
delete oldhead;
return ret;
}



> 
> 判断队列是否为空和其他问题
> 
> 
> 


判断是否为空,直接判断头部和尾部是否指向同一个节点



bool Empty()
{
return _head.load() == _tail.load();
}


因为我们的无锁队列是不能够被拷贝的,所以我们使用delete关键字直接将拷贝构造函数删除掉



LockFreeQueue(const LockFreeQueue&) = delete;


到这里,我们的无锁队列就实现完成了,之所以这么简单,还是因为C++库为我们提供了atomic这个类。无锁队列这里重在理解他的如何使用CAS的思想去更新节点的。


### 无锁队列全部源码和测试


经过笔者多次测试,发现无锁队列总体来说还是不错的,比加锁的队列能快一倍左右(第一行为无锁队列),这是插入10w数据的一次的结果,基本在这两个数字中波动。但是无锁队列对于线程数量的选择很关键。随着线程数增大,最后会比加锁队列更差,原因不难理解,CAS机制本身还是太消耗CPU资源了。


![在这里插入图片描述](https://img-blog.csdnimg.cn/20190922172025310.jpg)



#pragma once
#include
#include
#include
#include
#include
#include<condition_variable>
using namespace std;

template
class LockFreeQueue
{
struct Node
{
T _value;
std::atomic<Node*> _next;
Node(const T& x)
: _value(x)
, _next(nullptr)
{}
};

public:
LockFreeQueue()
{
_head = _tail = new Node(T());
}

LockFreeQueue(const LockFreeQueue<T>&) = delete;

~LockFreeQueue()
{
	Node\* cur = _head;
	while (cur)
	{
		Node\* next = cur->_next;
		delete cur;
		cur = next;
	}
}

void Enqueue(const T& x)
{
	Node\* newnode = new Node(x);
	Node\* oldtail = nullptr;
	Node\* nullnode = nullptr;
	do
	{
		oldtail = _tail.load();
	} while (oldtail->_next.compare\_exchange\_weak(nullnode, newnode) != true);
	_tail.compare\_exchange\_weak(oldtail, newnode);
}

T Dequeue()
{
	Node\* oldhead = _head.load();
	T headvalue;
	do
	{
		Node\* next = oldhead->_next;
		if (next == nullptr)
		{
			return T();
		}
		else
		{
			headvalue = next->_value;	
		}
	} 
	while (_head.compare\_exchange\_weak(oldhead, oldhead->_next) != true);

	delete oldhead;
	return headvalue;
}

bool Empty()
{
	return _head.load() == _tail.load();
}

private:
std::atomic<Node*> _head;
std::atomic<Node*> _tail;
};

mutex mutx;
int main()
{
LockFreeQueue lq;
queue nq;
atomic<size_t> n = 100000;

//测试无锁队列
{
	size_t costtime = 0;
	vector<thread> threads;
	for (size_t i = 0; i < 3; ++i)
	{
		threads.push\_back(thread([&]()
		{
			size_t begin = 0, end = 0;
			begin = clock();
			for (size_t j = 0; j < n; ++j)
			{
				lq.Enqueue(j);
			}
			end = clock();
			costtime += (end - begin);
		}));
	}
	for (auto& e : threads)
	{
		e.join();
	}
	cout << costtime << endl;
}

//测试有锁队列
{
	size_t costtime = 0;
	vector<thread> threads;
	for (size_t i = 0; i < 3; ++i)
	{
		threads.push\_back(thread([&]()
		{
			size_t begin = 0, end = 0;
			begin = clock();
			for (size_t j = 0; j < n; ++j)
			{
				mutx.lock();
				nq.push(j);
				mutx.unlock();
			}
			end = clock();
			costtime += (end - begin);
		}));
	}
	for (auto& e : threads)
	{
		e.join();
	}
	cout << costtime << endl;
}

return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值