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++ 老炮儿的技术栈43 分钟前
基于Qt实现轻量化本地音乐播放器
开发语言·c++·qt·c·播放器·音乐
qq_448011161 小时前
C语言中的柔性数组
c语言·开发语言·柔性数组
小白学大数据1 小时前
长周期爬虫的数据一致性:断点续爬 + 事务回滚保障采集质量
开发语言·爬虫·测试工具
zlinear数据采集卡1 小时前
D223上位机C#开发实战:从帧解析到波形显示的完整实现
开发语言·arm开发·嵌入式硬件·fpga开发·开源·c#
Poo_Chai2 小时前
QT emit信号后完整处理流程,包括槽函数响应流程
java·开发语言·数据库
瑞码空间2 小时前
Java 图形界面(GUI)完整知识点手册
java·开发语言·图形界面·swing
小努蛋2 小时前
linux编译openresty异常问题,lua_cjson.c:706:5: 错误: ‘for‘ 循环初始化声明只在 C99 或 C++ 模式下允许
开发语言·lua·openresty
qz_Serene3 小时前
C++:类和对象(上)
开发语言·c++
西安景驰电子3 小时前
《PTP精确时间协议系列》第一篇:从原理到应用,全面解读IEEE 1588
linux·运维·服务器·开发语言·网络·windows·php
ctlover3 小时前
Python文件操作
开发语言·python