打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
c++11 atomic_flag实现高效的mutex

 c++11线程库提供了std::mutex,方便地实现跨平台的互斥锁,提供了std::atomic模板实现原子操作。但标准中没有强制要求std::atomic模板的各种实例化保证提供的操作接口是真正的无锁实现,标准中要求保证是无锁实现的只有std::atomic_flag类,此类没有 lock unlock try_lock接口,只有test_and_set和clear。利用std::atomic_flag可以方便地实现跨平台的旋转锁。旋转锁的效率要比std::mutex高出很多,但CPU的使用率也要高出很多。为了兼具高效率和低CPU使用率,可以在加锁时先按旋转锁方式尝试一定的次数,如果指定次数尝试加锁失败再转而调用等待。以下是c++11的实现,按照通用做法,提供 lock unlock try_lock三个操作接口:

#ifndef __SPIN_MUTEX_H_

#define __SPIN_MUTEX_H_

#include <mutex>

#include <atomic>

#include <chrono>

#include <condition_variable>

class spin_lock

{

public:

    spin_lock()

    {

        flg_.clear(std::memory_order_release);

    }

    ~spin_lock() = default;

public:

    void lock()

    {

        for (; flg_.test_and_set(std::memory_order_acquire););

    }

    bool try_lock()

    {

        int _try_cnt = 100;

        for (; _try_cnt > 0 && flg_.test_and_set(std::memory_order_acquire); --_try_cnt);

        if (_try_cnt > 0)

            return true;

        return false;

    }

    void unlock()

    {

        flg_.clear(std::memory_order_release);

    }

private:

    spin_lock(const spin_lock&) = delete;

    spin_lock(spin_lock&&) = delete;

    spin_lock& operator=(const spin_lock&) = delete;

    spin_lock& operator=(spin_lock&&) = delete;

private:

    std::atomic_flag flg_;

};

class spin_mutex

{

public:

    spin_mutex()

    {

        flg_.clear(std::memory_order_release);

    }

    explicit spin_mutex(int _try_cnt)

        : try_cnt_(_try_cnt)

    {

        flg_.clear(std::memory_order_release);

    }

    ~spin_mutex() = default;

public:

    void lock()

    {

        for (int millis = 5;; millis = (millis < 100 ? millis << 2 : millis))

        {

            int _try_cnt = try_cnt_;

            for (; _try_cnt > 0 && flg_.test_and_set(std::memory_order_acquire); --_try_cnt);

            if (_try_cnt > 0)

                return;

            std::unique_lock<std::mutex> ulk(mut_);

            cond_.wait_for(ulk, std::chrono::milliseconds(millis));

        }

    }

    bool try_lock()

    {

        int _try_cnt = try_cnt_;

        for (; _try_cnt > 0 && flg_.test_and_set(std::memory_order_acquire); --_try_cnt);

        if (_try_cnt > 0)

            return true;

        return false;

    }

    void unlock()

    {

        flg_.clear(std::memory_order_release);

        cond_.notify_all();

    }

private:

    spin_mutex(const spin_mutex&) = delete;

    spin_mutex(spin_mutex&&) = delete;

    spin_mutex& operator=(const spin_mutex&) = delete;

    spin_mutex& operator=(spin_mutex&&) = delete;

private:

    std::mutex mut_;

    std::atomic_flag flg_;

    std::condition_variable cond_;

    int try_cnt_{ 200 };

};

#endif

        spin_lock是直接使用std::atomic_flag,lock时不停地尝试加锁直到成功,参见cppreference.com上的示例。

        spin_mutex类实现了和std::mutex一样的功能,加锁时先循环指定次数加锁,加锁成功即返回,加锁失败即进入等待,注意这里的条件变量等待只能使用wait_for并且等待的时间不能太长,因为条件变量有两个缺点:假唤醒和先唤醒再等待无法等待返回。因为我们的spin_mutex加解锁靠的仅仅是std::atomic_flag,mutex和condition_variable仅仅是用作一个等待作用以不至于CPU使用率太高,因此这里用wait_for就避免开了条件变量的两个缺点。wait_for等待的时间先是很短,尝试次数增多再增加等待时间,以减少CPU使用率且出现先唤醒再等待时不会等待很长时间。解锁的时候先解锁,再调用notify_all唤醒所有等待线程立即开始抢占。

--------------------- 

作者:mymodian 

来源:CSDN 

原文:https://blog.csdn.net/mymodian9612/article/details/53728176 

版权声明:本文为博主原创文章,转载请附上博文链接!

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Linux锁机制
如何实现C 中的多线程编程
C++:线程(std::thread)
C++11 并发指南三(std::mutex 详解)
互斥锁和信号量的区别
Linux 内核的同步机制,第 1 部分
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服