简言
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;
}