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();
            }
        }
    }
}
相关推荐
忒可君2 分钟前
C# winform 报错:类型“System.Int32”的对象无法转换为类型“System.Int16”。
java·开发语言
GuYue.bing13 分钟前
网络下载ts流媒体
开发语言·python
火云洞红孩儿18 分钟前
基于AI IDE 打造快速化的游戏LUA脚本的生成系统
c++·人工智能·inscode·游戏引擎·lua·游戏开发·脚本系统
StringerChen20 分钟前
Qt ui提升窗口的头文件找不到
开发语言·qt
数据小爬虫@26 分钟前
如何利用PHP爬虫获取速卖通(AliExpress)商品评论
开发语言·爬虫·php
java1234_小锋1 小时前
MyBatis如何处理延迟加载?
java·开发语言
FeboReigns1 小时前
C++简明教程(4)(Hello World)
c语言·c++
FeboReigns1 小时前
C++简明教程(10)(初识类)
c语言·开发语言·c++
学前端的小朱2 小时前
处理字体图标、js、html及其他资源
开发语言·javascript·webpack·html·打包工具
zh路西法2 小时前
【C++决策和状态管理】从状态模式,有限状态机,行为树到决策树(二):从FSM开始的2D游戏角色操控底层源码编写
c++·游戏·unity·设计模式·状态模式