198_C++\C_详细解析无锁队列的应用,基于CAS封装的无锁List【用到了CAS无锁策略,有点类似乐观锁的意思】

93 篇文章 3 订阅

执行:

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,导致当前线程误以为还是原来的值,然后操作就会导致一些异常出现。
例如,偶尔会发生:
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

扳手的海角

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值