执行:
g++ -std=c++11 -pthread -Wl,--no-as-needed queue.cpp
/*
* 2021.12.11
* *bool** __sync_bool_compare_and_swap(type *ptr, type oldval type newval, ...)
* type __sync_val_compare_and_swap(type *ptr, type oldval type newval, ...)
* 其实有锁和无锁就是我们平时所说的乐观锁和悲观锁:
*
* 加锁是一种悲观的策略,它总是认为每次访问共享资源的时候,总会发生冲突,所以宁愿牺牲性能(时间)来保证数据安全。
* 无锁是一种乐观的策略,它假设线程访问共享资源不会发生冲突,所以不需要加锁,因此线程将不断执行,不需要停止。
* 一旦碰到冲突,就重试当前操作直到没有冲突为止。
————————————————
版权声明:本文为CSDN博主「一只牛_007」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yizhiniu_xuyw/article/details/113918713
*/
#include <algorithm>
#include <vector>
#include <string>
#include <unordered_map>
#include <queue>
#include <functional>
#include <stack>
#include <iostream>
#include <unistd.h>
#include <thread>
#include <list>
using namespace std;
/*
* 说明:基于CAS封装的无锁List。
*/
template <typename T>//模板。使得容器中的元素不限于int类型
class LockfreeList
{
private:
std::list<T> list;
private:
volatile int mutex;
int lock;
int unlock;
public:
LockfreeList() :mutex(0), lock(0), unlock(1)
{
}
~LockfreeList()
{
}
/*
* 12.10
* 在这里上锁、解锁并没用用到 pthread_mutex_lock()和pthread_mutex_unlock()
*
* 而是用到了一种无锁的策略:CAS
* 无锁的策略使用一种叫做比较交换的技术(CAS Compare And Swap)来鉴别线程冲突,
* 一旦检测到冲突产生,就重试当前操作直到没有冲突为止。
*
* 就好比Lock()中mutex地址所指空间中的值,只要不是1,就一直重试对比操作,直到 ==1跳出循环。
*
* 上锁、解锁这里是动态变化的,&mutex是一个volatile类型的变量
* 将变量定义为volatile就表示告诉编译器这个变量可能会被竟想不到地改变,
* 它的初始值是0,每次使用到这个变量的时候,
* 此时这种情况下,编译器就不会去假设这个变量的值了,
* 及优化器在用到这个变量是必须每次重新读取他的值,
* 所以为了安全起见,只要是等待别的程序修改某个变量的话,就加上【volatile】关键字。
*
* 这里由于使用了比较交换的技术-》CAS策略,在Lock()中会将mutex地址所指空间中的值修改为1
* 但是在初始化列表中,mutex值是0
* 所以为了防止Unlock()中的CAS策略用到的是初始化列表中的mutex值
* 这里就需要加上【volatile】关键字
*/
void Lock()
{
while (!__sync_bool_compare_and_swap(&mutex, lock, 1))
{
usleep(1);
}
}
void Unlock()
{
//上锁后,mutex地址所指空间中 的值变成1,这里就不需要在循环判断了
__sync_bool_compare_and_swap(&mutex, unlock, 0);
}
void Push(T data)
{
//在一切用到比较交换的技术-》CAS策略的函数中,mutex地址所指空间中的值是动态变化的
Lock();
list.push_back(data);
Unlock();
}
T Front()
{
Lock();
T data = list.front();
Unlock();
return data;
}
void PopFront()
{
Lock();
list.pop_front();
Unlock();
}
bool IsEmpty()
{
Lock();
if (list.empty())
{
Unlock();
return true;
}
else
{
Unlock();
return false;
}
}
bool Find(T data)
{
typename std::list<T>::iterator it;
Lock();
for (it = list.begin(); it != list.end(); ++it)
{
if (*it == data)
{
Unlock();
return true;
}
}
Unlock();
return false;
}
};
LockfreeList<int> LF;
thread_local int num = 1;
//生产者
void Producer()
{
while(true)
{
num++;
cout<<"num push:"<<num<<endl;
LF.Push(num);
sleep(2);
}
}
//消费者
void Customer()
{
while(true)
{
if (!LF.IsEmpty())
{
cout <<"num get " <<LF.Front() <<endl;
LF.PopFront();
}
sleep(1);
}
}
int main()
{
thread t1(Producer);
thread t2(Customer);
thread t3(Customer);
t1.join();
t2.join();
t3.join();
return 0;
}
CAS策略的一些小小问题:
其实在CAS中,还有一种异常产生,也就是常说的ABA的现象。所谓ABA现象就是当前现象期望值是A,某个线程将A改为B,另外线程将B改为A,导致当前线程误以为还是原来的值,然后操作就会导致一些异常出现。
例如,偶尔会发生: