C++ SpinLock、RecursiveSpinLock 基于原子CAS实现自旋锁(TASLock、TTASLock)

本文使用内存屏障来 VolatileRead、VolatileWrite 保证读写重入自旋锁的当前线程ID(tid),如果不愿意采用内存屏障的方式来处理,人们可以选择使用原子变量读写来代替。

C++ 置R/W内存屏障:

cpp 复制代码
            static inline void							MemoryBarrier() noexcept
            {
                std::atomic_thread_fence(std::memory_order_seq_cst);
            }

头文件:

cpp 复制代码
#pragma once

#include <ppp/stdafx.h>

namespace ppp
{
    namespace threading
    {
        class SpinLock final
        {
        public:
            explicit SpinLock() noexcept;
            SpinLock(const SpinLock&) = delete;
            SpinLock(SpinLock&&) = delete;
            ~SpinLock() noexcept(false);

        public:
            SpinLock&                   operator=(const SpinLock&) = delete;

        public:
            bool                        TryEnter() noexcept;
            bool                        TryEnter(int loop, int timeout) noexcept;
            void                        Enter() noexcept;
            void                        Leave();
            inline bool                 IsLockTaken() noexcept { return _.load(); }

        public:
            inline void                 lock() noexcept { Enter(); }
            inline void                 unlock() noexcept { Leave(); }

        public:
            std::atomic<bool>           _;
        };

        class RecursiveSpinLock final
        {
        public:
            explicit RecursiveSpinLock() noexcept;
            RecursiveSpinLock(const RecursiveSpinLock&) = delete;
            RecursiveSpinLock(RecursiveSpinLock&&) = delete;
            ~RecursiveSpinLock() = default;

        public:
            RecursiveSpinLock&          operator=(const RecursiveSpinLock&) = delete;

        public:
            bool                        TryEnter() noexcept;
            bool                        TryEnter(int loop, int timeout) noexcept;
            void                        Enter() noexcept;
            void                        Leave();
            inline bool                 IsLockTaken() noexcept { return lockobj_.IsLockTaken(); }

        public:
            inline void                 lock() noexcept { Enter(); }
            inline void                 unlock() noexcept { Leave(); }

        public:
            SpinLock                    lockobj_;
            volatile int                tid_;
            std::atomic<int>            reentries_;
        };
    }
}

源文件:

cpp 复制代码
#include <ppp/threading/SpinLock.h>
#include <ppp/threading/Thread.h>

namespace ppp
{
    namespace threading
    {
        template <class LockObject>
        static constexpr bool Lock_TryEnter(
            LockObject&                                             lock, 
            int                                                     loop,
            int                                                     timeout) noexcept
        {
            bool lockTaken = false;
            if (loop > -1)
            {
                uint64_t last = GetTickCount();
                for (int i = 0; i < loop; i++)
                {
                    lockTaken = lock.TryEnter();
                    if (lockTaken)
                    {
                        break;
                    }

                    if (timeout > -1)
                    {
                        uint64_t now = GetTickCount();
                        int64_t diff = now - last;
                        if (diff >= timeout)
                        {
                            break;
                        }
                    }
                }
            }
            else
            {
                uint64_t last = GetTickCount();
                for (;;)
                {
                    lockTaken = lock.TryEnter();
                    if (lockTaken)
                    {
                        break;
                    }

                    if (timeout > -1)
                    {
                        uint64_t now = GetTickCount();
                        int64_t diff = now - last;
                        if (diff >= timeout)
                        {
                            break;
                        }
                    }
                }
            }

            return lockTaken;
        }

        template <class LockObject, class LockInternalObject, typename... TryEnterArguments>
        static constexpr bool RecursiveLock_TryEnter(LockObject&    lock, 
            LockInternalObject&                                     lock_internal, 
            volatile int*                                           tid,
            std::atomic<int>&                                       reentries, 
            TryEnterArguments&&...                                  arguments)
        {
            int n = ++reentries;
            assert(n > 0);

            int current_tid = std::hash<std::thread::id>{}(std::this_thread::get_id());
            if (n == 1)
            {
                bool lockTaken = lock_internal.TryEnter(std::forward<TryEnterArguments>(arguments)...);
                if (!lockTaken)
                {
                    reentries--;
                    return false;
                }

                Thread::MemoryBarrier();
                *tid = current_tid;
                Thread::MemoryBarrier();
            }
            else
            {
                Thread::MemoryBarrier();
                int lockTaken_tid = *tid;
                Thread::MemoryBarrier();

                if (lockTaken_tid == current_tid)
                {
                    lock.Leave();
                    return false;
                }
            }

            return true;
        }

        SpinLock::SpinLock() noexcept
            : _(false)
        {

        }

        SpinLock::~SpinLock() noexcept(false)
        {
            bool lockTaken = IsLockTaken();
            if (lockTaken)
            {
                throw std::runtime_error("fail to release the atomic lock.");
            }
        }

        void SpinLock::Enter() noexcept
        {
            for (;;) 
            {
                bool lockTaken = TryEnter();
                if (lockTaken)
                {
                    break;
                }
            }
        }

        bool SpinLock::TryEnter(int loop, int timeout) noexcept
        {
            return Lock_TryEnter(*this, loop, timeout);
        }

        bool SpinLock::TryEnter() noexcept
        {
            bool expected = false;
            return _.compare_exchange_strong(expected, true, std::memory_order_acquire);
        }

        void SpinLock::Leave()
        {
            bool expected = true;
            if (!_.compare_exchange_strong(expected, false, std::memory_order_release))
            {
                throw std::runtime_error("failed to acquire the atomic lock.");
            }
        }

        RecursiveSpinLock::RecursiveSpinLock() noexcept
            : lockobj_()
            , tid_(0)
            , reentries_(0)
        {

        }

        bool RecursiveSpinLock::TryEnter() noexcept
        {
            return RecursiveLock_TryEnter(*this, lockobj_, &tid_, reentries_);
        }

        bool RecursiveSpinLock::TryEnter(int loop, int timeout) noexcept
        {
            return RecursiveLock_TryEnter(*this, lockobj_, &tid_, reentries_, loop, timeout);
        }

        void RecursiveSpinLock::Enter() noexcept
        {
            for (;;)
            {
                bool lockTaken = TryEnter();
                if (lockTaken)
                {
                    break;
                }
            }
        }

        void RecursiveSpinLock::Leave() 
        {
            int n = --reentries_;
            assert(n >= 0);

            if (n == 0)
            {
                lockobj_.Leave();
            }
        }
    }
}
相关推荐
C++ 老炮儿的技术栈39 分钟前
UDP 与 TCP 的区别是什么?
开发语言·c++·windows·算法·visual studio
wgslucky44 分钟前
Dubbo报错:module java.base does not “opens java.lang“ to unnamed module
java·开发语言·dubbo
whyeekkk1 小时前
python打卡第48天
开发语言·python
DougLiang2 小时前
关于easyexcel动态下拉选问题处理
java·开发语言
mochensage2 小时前
CSP信奥赛C++常用系统函数汇总
c++·信奥
mochensage2 小时前
C++信息学竞赛中常用函数的一般用法
java·c++·算法
fpcc2 小时前
跟我学c++中级篇——多线程中的文件处理
c++
全职计算机毕业设计3 小时前
基于Java Web的校园失物招领平台设计与实现
java·开发语言·前端
5:003 小时前
云备份项目
linux·开发语言·c++
笨笨马甲4 小时前
Qt Quick模块功能及架构
开发语言·qt