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();
            }
        }
    }
}
相关推荐
No0d1es8 分钟前
2024年9月青少年软件编程(C语言/C++)等级考试试卷(七级)
c语言·开发语言·c++·算法·青少年编程·电子学会·七级
二十雨辰10 分钟前
[Java]微服务拆分
java·开发语言
周杰伦fans11 分钟前
C#中object和dynamic
开发语言·c#
weixin_SAG15 分钟前
14天Java基础学习——第6天:面向对象编程(类与对象)
java·开发语言·学习
暮色_年华17 分钟前
More effective C++:效率(2)
c++
c_simplystudy17 分钟前
c++ 类和对象(中)
开发语言·c++·学习
尘浮生20 分钟前
Jav项目实战II基于微信小程序的助农扶贫的设计与实现(开发文档+数据库+源码)
java·开发语言·数据库·微信小程序·小程序·汽车·旅游
王燕龙(大卫)20 分钟前
std::cout打印空的char指针会出现未定义行为
开发语言·c++
钰爱&21 分钟前
【C】链表的创建、打印、插入、删除等
c语言·开发语言·数据结构·笔记·学习·链表
刽子手发艺1 小时前
JVM双亲委派机制详解
java·开发语言·jvm