首页 > 其他分享 >Lock Free 无锁队列的实现

Lock Free 无锁队列的实现

时间:2024-11-13 16:41:12浏览次数:1  
标签:无锁 ListNode CAS Lock Free next tail 线程 bool

无锁队列的实现

 

无锁队列的实现原理一般是利用 Retry-loop 和 CAS 等原子操作。

现在几乎所有的CPU指令都支持CAS的原子操作,X86下对应的是 CMPXCHG 汇编指令。

例如 CAS(Compare And Swap)的实现原理:

bool compare_and_swap (int *addr, int oldval, int newval)
{
  if ( *addr != oldval ) {
      return false;
  }
  *addr = newval;
  return true;
}

与CAS相似的还有下面的原子操作:

1、Fetch And Add,一般用来对变量做+1的原子操作; 2、Test And Set,写值到某个内存位置并传回其旧值; 3、Test And Test-and-set;   GCC 的CAS实现版本如下:
bool __sync_bool_compare_and_swap(type *ptr, type oldval, type newval, ...)

可参考http://gcc.gnu.org/onlinedocs/gcc-4.1.1/gcc/Atomic-Builtins.html

  C++11 的CAS实现版本如下:
template<class T> 
bool atomic_compare_exchange_weak(atomic<T>* obj, T* expected, T desired);

 

看一个无锁队列的实现例子:

class QueueWithoutLock {
public:
    QueueWithoutLock() {
        dummy = new ListNode(-1);
        head = tail = dummy;
    }

    ~QueueWithoutLock() {}

    bool Push(uint64_t val) {
        auto* node = new ListNode(val);
        ListNode* oldTail = nullptr;
        do {
            oldTail = tail;  // 如果其它线程 push 成功,这里 tail 可能发生变化,所以重新取值一下
        } while (!__sync_bool_compare_and_swap(&(oldTail->next), nullptr, node));

        // 更新 tail 到新节点
        // 这里没有判断成功,是因为走到这里,表示上面成功 push 了,在更新 tail 之前其 next 不再是 null,其它线程的 push 操作都会失败
        __sync_bool_compare_and_swap(&tail, oldTail, node);
        return true;
    }

    ListNode* Pop() {
        ListNode *p;
        do {
            p = head;
            if (p->next == NULL) {
                // queue is empty
                return nullptr;
            }
        } while (!__sync_bool_compare_and_swap(&head, p, p->next));     // 如果 head != p(说明其它线程修改了 head),循环继续
        return p->next;
    }

private:
    ListNode* dummy;
    ListNode* head;
    ListNode* tail;
};

 

这里有一个潜在的问题——如果某个线程在用 CAS 更新 tail 指针之前,线程停掉或是挂掉了,那么其它线程就进入死循环了。下面是改良版的Push()

bool Push(uint64_t val) {
    auto* node = new ListNode(val);
    ListNode* oldTail = tail;       // 取链表尾指针的快照
    ListNode* p = tail;
    do {
        while (p->next) p = p->next;     // 每次循环里面重新 fetch 尾指针,不依赖 tail 成员
    } while (!__sync_bool_compare_and_swap(&(p->next), nullptr, node));

    // 重置 tail 指针
    __sync_bool_compare_and_swap(&tail, oldTail, node);
    return true;
}

 

 

CAS 导致的 ABA 问题

 

有2个线程 a、b,

  1. 线程 a 从共享的地址X中读取到了对象A。
  2. 在线程 a 准备对地址X进行更新之前,线程b将地址X中的值修改为了B。
  3. 接着线程 b 将地址X中的值又修改回了A。
  4. 最新线程 a 对地址X执行 CAS,发现X中存储的还是对象A,对象匹配,CAS成功。

在有 GC环境的编程语言比如说java中,2,3的情况是不可能出现的,因为在java中,只要两个对象的地址一致,就表示这两个对象是相等的。

但在 C++ 中,可以自己控制对象的生命周期,如果我们从一个 list 中删除掉了一个对象,然后又重新分配了一个对象,并将其add back到 list 中去,那么根据 MRU memory allocation算法,这个新的对象很有可能和之前删除对象的内存地址是一样的。这样就会导致ABA的问题。

解决办法:

1、使用 风险指针(hazard pointer),线程a 读出对象A的同时,将对象A的指针放入到自己的 hp 链表中,这样可以保证这个对象不会被删除;

2、使用 read-copy update (RCU) ,在每次更新的之前,都做一份拷贝,每次更新的是拷贝出来的新结构。

 

 

 

 

标签:无锁,ListNode,CAS,Lock,Free,next,tail,线程,bool
From: https://www.cnblogs.com/chenny7/p/16791988.html

相关文章

  • JUC-locks锁
    JUC-locks锁1、JUC-locks锁概述2、管程模型3、ReentrantLock可重入锁3.1ReentrantLock源码3.2Sync静态内部类3.3NonfairSync非公平锁3.4FairSync公平锁如有侵权,请联系~如有错误,也欢迎批评指正~1、JUC-locks锁概述java的并发包【JUC】下面就两个子包,一个是atom......
  • 深入探索ReentrantLock(四):公平与非公平锁的双重奏
    前言在并发编程中,锁是管理共享资源访问的关键机制之一。Java并发包(java.util.concurrent)中的ReentrantLock类提供了一个比内置synchronized关键字更灵活的锁实现。它不仅支持重入性(即同一个线程可以多次获得锁),还提供了公平锁和非公平锁两种模式,以满足不同场景下的需求。本文将......
  • Freesql、SqlSugar测试有感
    突然心血来潮测试了一下Freesql和SqlSugar的批量插入和批量更新性能,一搜测评一大堆,但是没找到自己想要的结果,自己动手测试一下基本的批量插入和批量更新性能。废话不多说直接贴代码1usingFreeSql;2usingFreeSql.DataAnnotations;3usingSqlSugar;45namesp......
  • 在通讯领域,特别是在自由空间光通信(Free Space Optics, FSO)通道模拟中,选择合适的模型需
    在通讯领域,特别是在自由空间光通信(FreeSpaceOptics,FSO)通道模拟中,选择合适的模型需要考虑模型对动态变化的光信号传播环境的适应性和预测能力。根据搜索结果,以下是一些可能适合通讯领域FSO通道模拟的模型:TACTiS-2:这是一个灵活的多变量概率时间序列预测模型,它简化了attenti......
  • Free5GC源码研究(9) - PCF研究(下)
    前文再续书接上一回,继续研究Free5GC中所实现的PCF的另外两组服务:SMPolicy和PolicyAuthorizationSMPolicyPCF中与SMF的交互,对session的控制有着很重的的分量,甚至连TS23.503中对与PolicyControl的定义都是指PCF指示SMF去控制QoS流的过程。Policycontrol:Theprocesswhereb......
  • Freemarker模板 jar!/BOOT-INF/classes!/**.html
    需求:发送邮件,邮件内容通过Freemaker模板生成,如下代码:Configurationconfiguration=newConfiguration(Configuration.getVersion());configuration.setDefaultEncoding("utf-8");/**加载模板目录**///这个方法在IDEA跑是OK的Filefile=ResourceUtils.getFile("clas......
  • 【病毒分析】Lockbit 3.0勒索病毒加密程序分析
    1.背景   在2022年,LockBit是全球规模最大的勒索软件变种,且在2023年继续肆虐。自2020年1月以来,使用LockBit的附属机构已针对各个规模的关键基础设施领域的组织进行了攻击,包括金融服务、食品和农业、教育、能源、政府和紧急服务、医疗保健、制造和交通等。LockBit勒索软件......
  • AutoCAD Blockview .net在wpf项目中的问题
    之前使用Blockview是遇到平移的问题,这几天在学习使用CommunityToolkit.MVVM框架来创建用户界面,当创建GsPreviewCtrl控件时会遇到错误,导致整个窗体不能显示,错误信息如下:**************异常文本**************System.InvalidProgramException:公共语言运行时检测到无效的......
  • py-filelock python 平台无关的文件锁
    py-filelock是一个平台无关的文件锁实现,可以用来实现一些基于文件锁的业务控制参考使用lock.pyimportosfromfilelockimportTimeout,FileLockfile_path="high_ground.txt"lock_path="high_ground.txt.lock"lock=FileLock(lock_path,timeout=1)withlock:......
  • FreeRTOS 24:事件组EventGroup等待、清零、获取操作
    等待事件标志位xEventGroupWaitBits()既然标记了事件的发生,那么我怎么知道他到底有没有发生,这也是需要一个函数来获取事件是否已经发生,FreeRTOS提供了一个等待指定事件的函数——xEventGroupWaitBits(),通过这个函数,任务可以知道事件标志组中的哪......