C++并发实战19:lock free编程

C++并发实战19:lock free编程涉及到并行/并发计算时,通常都会想到加锁,加锁可以保护共享的数据,不过也会存在一些问题:1.由于临界区无法并发运行,进入临界区就需要等待,加锁使得效率的降低。多核CPU也不能发挥全部马力2.在复杂的情况下,很容易造成死锁,并发进程、线程之间无止境的互相等待。3.在中断/信号处理函数中不能加锁,给并发处理带来困难。4.加锁影响实时性,等待时间不确定5.优先级反转,优先级

大家好,又见面了,我是你们的朋友全栈君。     涉及到并行/并发计算时,通常都会想到加锁,加锁可以保护共享的数据,不过也会存在一些问题:

1. 由于临界区无法并发运行,进入临界区就需要等待,加锁使得效率的降低。多核CPU也不能发挥全部马力

2. 在复杂的情况下,很容易造成死锁,并发进程、线程之间无止境的互相等待。

3. 在中断/信号处理函数中不能加锁,给并发处理带来困难。

4. 加锁影响实时性,等待时间不确定

5. 优先级反转,优先级高的等待优先级低的

6. 若一个线程带着锁挂了,那么将会影响其它等待该锁的线程

总之,在基于锁的多线程/多进程编程,你需要保证对竞争条件很敏感的共享数据上的任何操作,都通过加锁或解锁一个独占(mutex)来实现原子操作。

现有的加锁/无锁编程的种类如下:

     C++并发实战19:lock free编程

      其中标注为红色字体的方案为 Blocking synchronization需要锁,黑色字体为 Non-blocking synchronization无锁。Lock-based 和 Lockless-based 两者之间的区别仅仅是加锁粒度的不同。图中最底层的方案就是大家经常使用的 mutex 和 semaphore 等方案,代码复杂度低,但运行效率也最低。

      可以使用std::atomic实现lock free,但这里并不是真正的无锁,只有atomic_flag是无锁的,其它的atomic内部都是有锁的只不过粒度很小.atomic::compare_exchange_weak/strong等于是个CAS(比较并交换)操作,在C++11之前该操作是平台相关的,现在atomic将其实现为成员函数。

      一个lock free的栈:

#include <atomic>
#include <memory>

template<typename T>
class lock_free_stack//栈的底层数据结构采用单向链表实现
{
private:
    struct node
    {
        std::shared_ptr<T> data;//这里采用shared_ptr管理的好处在于:若栈内存放对象pop中return栈顶对象可能拷贝异常,栈内只存储指针还可以提高性能
        node* next;
        node(T const& data_):
            data(std::make_shared<T>(data_))//注意make_shared比直接shared_ptr构造的内存开销小
        {}
    };
    std::atomic<node*> head;//采用原子类型管理栈顶元素,主要利用atomic::compare_exchange_weak实现lock free
public:
    void push(T const& data)
    {
        node* const new_node=new node(data);
        new_node->next=head.load();//每次从链表头插入
        while(!head.compare_exchange_weak(new_node->next,new_node));//若head==new_node->next则更新head为new_node,返回true结束循环,插入成功; 若head!=new_node->next表明有其它线程在此期间对head操作了,将new_node->next更新为新的head,返回false,继续进入下一次while循环。这里采用atomic::compare_exchange_weak比atomic::compare_exchange_strong快,因为compare_exchange_weak可能在元素相等的时候返回false所以适合在循环中,而atomic::compare_exchange_strong保证了比较的正确性,不适合用于循环
    }
    std::shared_ptr<T> pop()
    {
        node* old_head=head.load();//拿住栈顶元素,但是可能后续被更新,更新发生在head!=old_head时
        while(old_head &&!head.compare_exchange_weak(old_head,old_head->next));//这里注意首先要先判断old_head是否为nullptr防止操作空链表,然后按照compare_exchange_weak语义更新链表头结点。若head==old_head则更新head为old_head->next并返回true,结束循环,删除栈顶元素成功;若head!=old_head表明在此期间有其它线程操作了head,因此更新old_head为新的head,返回false进入下一轮循环,直至删除成功。
        return old_head ? old_head->data : std::shared_ptr<T>();//这里注意空链表时返回的是一个空的shared_ptr对象
    }//这里只是lock free,由于while循环可能无限期循环不能在有限步骤内完成,故不是wait free
};

     

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/161734.html原文链接:https://javaforall.net

(0)
上一篇 2022年7月19日 上午10:00
下一篇 2022年7月19日 上午10:00


相关推荐

  • 堆栈溢出及其原因

    堆栈溢出及其原因在一个程序中 分配给堆栈的内存是有一定限度的 由于 C 语言系列没有内置检查机制来确保复制到缓冲区的数据不得大于缓冲区的大小 因此当这个数据足够大的时候 将会溢出缓冲区的范围 堆栈溢出就是不顾堆栈中分配的局部数据块大小 向该数据块写入了过多的数据 导致数据越界 结果覆盖了别的数据 堆栈溢出时会有意想不到的结果发生 甚至程序会运行崩溃 一般来说 造成堆栈溢出的原因主要有以下几个 1 开了数据非常

    2026年3月18日
    1
  • Mybatis的多表关联查询(多对多)「建议收藏」

    Mybatis的多表关联查询(多对多)「建议收藏」Mybatis的多表关联查询(多对多)项目目录结构实现Role到User多对多业务要求用户与角色的关系模型编写角色实体类编写Role持久层接口实现的SQL语句编写映射文件测试代码实现User到Role的多对多业务要求编写用户实体类编写User持久层接口实现的SQL语句编写映射文件测试代码mybatis中的多表查询: 示例:用户和角色 一个用户可以有多个角色 一个角色可以赋予多个用户 步骤: 1、建立两张表:用户表,角色表 让用户表和角色表具有多对多的关

    2022年5月1日
    56
  • JavaScript split() 方法的第二个参数

    JavaScript split() 方法的第二个参数记录目的补充记录冷门知识点 记录日常工作使用 split 将字符串分割为数组 基本只会使用一个参数 即分割字符串的字符 如空字符 空格 逗号 特殊符号 amp 等等 查看官方 api 可以看到此方法拥有第二个参数 howmany 根据传入的 howmany 数值 返回切割后的长度为 howmany 的数组 使用场景比较少 但懂得多一点总归是好的 示例场景 只需要切割后的前 n 项 数据存在规律性 从某个分割点之后无效 获取有效数据 或是装 x 显摆 或是欺负小白 你

    2026年3月19日
    3
  • laravel 5.4 导出excel表格

    laravel 5.4 导出excel表格

    2021年10月24日
    46
  • JWT详解「建议收藏」

    JWT详解「建议收藏」本文从本人博客搬运,原文格式更加美观,可以移步原文阅读:JWT详解JWT简介1.什么是JWT在介绍JWT之前,我们先来回顾一下利用token进行用户身份验证的流程:客户端使用用户名和密码请求登录服务端收到请求,验证用户名和密码验证成功后,服务端会签发一个token,再把这个token返回给客户端客户端收到token后可以把它存储起来,比如放到cookie中客户端每次向服务端请求资源时需要携带服务端签发的token,可以在cookie或者header中携带服务端收到请求,然后去验证客户端请

    2022年5月11日
    42
  • nodejs文心一言API接入

    nodejs文心一言API接入

    2026年3月12日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号