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

     

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • javascript 手机号码正则表达式验证函数

    javascript 手机号码正则表达式验证函数随着手机号码段的不断增加,以前网上的手机号码验证函数都不能那么完美的支持了,这里脚本之家编辑特为大家准备的一个简单的正则与手机验证的函数分析。functioncheckMobile(){varsMobile=document.mobileform.mobile.valueif(!(/^1[3|4|5|8][0-9]\d{4,8}$/.test(sMobile))

  • pycharm2021linux 激活码【在线注册码/序列号/破解码】

    pycharm2021linux 激活码【在线注册码/序列号/破解码】,https://javaforall.cn/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

  • idea和pycharm哪个好用_chrome实用的插件

    idea和pycharm哪个好用_chrome实用的插件Idea及Pycharm插件安装方式:File->Setting->Plugins1.Translation(翻译插件)2.RegexTester(正则表达式测试)3.JsonParser(验证和格式化JSON字符)4.MongoQuery(数据库连接及可视化)

  • HDU4907小技巧

    HDU4907小技巧

  • PyCharm 2021.5.3 x64激活码[在线序列号]

    PyCharm 2021.5.3 x64激活码[在线序列号],https://javaforall.cn/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

  • 安装SPSS22_SPSS 24安装教程

    安装SPSS22_SPSS 24安装教程学习统计学必不可少的一个软件就是SPSS,这个软件非常的重要,但是随着时代的发展貌似又开始逐渐变得有点鸡肋,然而,我们还是要学,没辙,哈哈哈哈~接下来就跟着我一起安装一个SPSS22吧!首先你需要一个Windows电脑(Mac再说,风紧扯呼),最好是win10,然后在百度云下载必要的程序与秘钥百度云提取码:kp68里面包括exeandtxt下载下来后打开文件夹进入SPSS文件夹右击此文件,选择“以管理员权限运行”点击“下一步”选择“的那个用户许可证”,点击“下一步”选择“我接受许

    2022年10月23日

发表回复

您的电子邮箱地址不会被公开。

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