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();
            }
        }
    }
}
相关推荐
江湖行骗老中医4 分钟前
js闭包概念和使用
开发语言·javascript·ecmascript
FAREWELL000756 分钟前
C#进阶学习(十七)PriorityQueue<TElement, TPriority>优先级队列的介绍
开发语言·学习·c#·优先级队列
NicOym18 分钟前
进程、线程、进程间通信Unix Domain Sockets (UDS)
linux·c++·unix
海绵宝宝的月光宝盒28 分钟前
[stm32] 4-1 USART(1)
c语言·开发语言·笔记·stm32·单片机
九班长33 分钟前
JMeter WebSocket 压测详细步骤(支持 ws+proto 协议)
开发语言·python·网络协议·jmeter·golang
我真不困了1 小时前
类和对象(下)
c++·笔记·学习·其他
小安同学iter1 小时前
苍穹外卖心得体会
java·开发语言·spring boot·servlet·intellij-idea·mybatis
chunfeng—1 小时前
纯C协程框架NtyCo
linux·c++·后端·协程·ntyco
LWing6512 小时前
算法基础学习|02归并排序——分治
c++·学习·算法
秋风&萧瑟3 小时前
【QT】QT控制硬件
开发语言·qt