c++无锁链表的实现

简言

1.无锁能够实现的核心原理即CAS

2. c++语言中CAS一般有三种操作,即gcc的CAS实现,windows的CAS实现,c++11的CAS实现

3. 这篇博客主要讲c++11的CAS实现

4. 具体原理分析可参考:https://blog.csdn.net/yzf279533105/article/details/119798571

实验过程

1. 预先加入10000个节点,加上一个空节点,一共10001个节点

2. 开500个线程增加10万节点,同时开500个线程删除10万节点

3. 打印链表长度,发现仍然是10001

实验如下图

完整代码

#include <iostream>
#include <atomic>
#include <thread>
#include <unistd.h>
#include <stdlib.h>

using namespace std;

// 节点
struct Node 
{
	int				value;
	atomic<Node*>	next;
};

// 无锁链表
struct NoLockList
{
	atomic<Node*> pHead;
	atomic<Node*> pTail;

	void enqueueOne(int thread, int n, bool stop);				// 入队
	int  dequeueOne(int thread, bool stop);						// 出队
	void enqueueMany(int thread, int m, bool stop);	// 入队多个节点
	void dequeueMany(int thread, int m, bool stop); // 出队多个节点

	int  len();				// 长度
};

// 压入队列一个节点
void NoLockList::enqueueOne(int thread, int n, bool stop)
{
	// 新建节点
	Node* pnew	= new Node();
	pnew->value		= n;
	pnew->next		= NULL;

	Node* ptail		= NULL;
	Node* ptailnext	= NULL;
	Node* pNULL		= NULL;

	while (true)
	{
		ptail		= pTail; // 每次拿队列最新的Tail
		ptailnext	= pTail.load()->next;

		// 若pTail不是真正链表末尾,则后移pTail
		// 注意此步不可简单地continue,因为其他线程可能因为某种原因导致只加入了新节点,未能更新Tail指针,这里可以修正
		if (ptailnext != NULL)
		{
			pTail.compare_exchange_weak(ptail, ptailnext);
			cout<<"enqueueOne::ptailnext != NULL, thread="<<thread<<endl;
			continue;
		}

		if (stop)
		{
			usleep(500+rand()%500);
		}

		// 核心:若pTail->next为空,则确定pTail是链表尾节点,则pTail->next改为指向新节点,修改失败则重试
		if (pTail.load()->next.compare_exchange_weak(pNULL, pnew))
		{
			// 更新尾指针pTail,即使更新失败也没关系,说明其他线程已经更新了pTail
			pTail.compare_exchange_weak(ptail, pnew);
			cout<<"enqueueOne,suc, thread="<<thread<<endl;
			return;
		}
		cout<<"enqueueOne::retry"<<endl;
	}
}

// 出队列
int NoLockList::dequeueOne(int thread, bool stop) 
{
	Node* phead		= NULL;
	Node* ptail		= NULL;
	Node* pheadNext	= NULL;

	while (true)
	{
		phead		= pHead;
		ptail		= pTail;
		pheadNext	= phead->next;

		// 链表为空(只有一个初始节点)
		if (pheadNext==NULL)
		{
			cout<<"dequeueOne::pheadNext==NULL"<<endl;
			return -1;
		}

		// 链表不为空,但pHead指针和pTail相等,说明pTail未能及时更新,则后移pTail
		if (phead == ptail)
		{
			cout<<"dequeueOne::phead == ptail, thread="<<thread<<endl;
			pTail.compare_exchange_weak(ptail,pheadNext);
			continue;
		}

		// 随机暂停一段时间
		if (stop)
		{
			usleep(500+rand()%500);
		}

		// 核心:若pList->pHead确定是链表头部节点,则后移Head;失败则重试
		if ( pHead.compare_exchange_weak(phead, pheadNext))
		{
			int val = phead->value;
			free(phead);
			cout<<"dequeueOne,suc, thread="<<thread<<endl;
			return val;
		}

		cout<<"dequeueOne::retry, thread="<<thread<<endl;
	}
}

// 入队多个节点
void NoLockList::enqueueMany(int thread, int m, bool stop)
{
	for (int i=0;i<m;i++)
	{
		enqueueOne(thread, i,stop);
	}
}

// 出队多个节点
void  NoLockList::dequeueMany(int thread, int m, bool stop )
{
	for (int i=0;i<m;i++)
	{
		dequeueOne(thread, stop);
	}
}

// 队列长度
int NoLockList::len() 
{
	if (pHead.load() == pTail.load())
	{
		return 1;
	}

	int le = 1;
	Node* p = pHead;
	while (p->next.load())
	{
		p = p->next.load();
		le++;
	}

	return le;
}

int main() 
{
	NoLockList list;

	// 初始化第一个空节点
	Node* pNew	= new Node();
	pNew->value		= 0;
	pNew->next		= NULL;
	list.pHead = pNew;
	list.pTail = pNew;

	// 10万个节点
	const int nodeNum = 100000;
	// 1000个线程
	const int threadNum = 1000;

	// 初始10000节点
	for (int i=0;i<10000;i++)
	{
		list.enqueueOne(0, i,false);
	}

	cout<<"start, list elements is : "<<list.len()<<endl;

	// 多个线程进行入队,出队操作
	thread en[threadNum];
	thread de[threadNum];
	for (int i=0;i<threadNum;i++)
	{
		en[i] =  thread(&NoLockList::enqueueMany, &list, i, nodeNum/threadNum,true);
		de[i] =  thread(&NoLockList::dequeueMany, &list, i, nodeNum/threadNum,true);
	}
	for (int i=0;i<threadNum;i++)
	{
		de[i].join();
		en[i].join();
	}

	cout<<"end, list elements is : "<<list.len()<<endl;

	return 0;
}

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值