首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >读者作家SpinLock

读者作家SpinLock
EN

Code Review用户
提问于 2019-02-19 16:51:53
回答 1查看 408关注 0票数 4

我对C++非常陌生,我想专注于编写性能良好的多线程代码,因为我将尝试移植我们公司的内部GUI框架,该框架目前是用C#实现的。因此,我想获得一些关于代码风格和性能的建议,这些建议对我所写的读者-作者自旋锁是有利的:

代码语言:javascript
复制
namespace UI
{
    namespace Threading
    {
        /**
         * \brief ReaderWriterSpinLock which favors writers
         */
        class ReaderWriterSpinLock
        {
        public:
            ReaderWriterSpinLock() = default;
            ~ReaderWriterSpinLock() = default;
            ReaderWriterSpinLock(ReaderWriterSpinLock&) = delete;
            ReaderWriterSpinLock(ReaderWriterSpinLock&&) = delete;
            ReaderWriterSpinLock& operator =(ReaderWriterSpinLock&) = delete;
            ReaderWriterSpinLock& operator =(ReaderWriterSpinLock&&) = delete;

            void AcquireReaderLock()
            {
                int computedValue, initialValue;
                do
                {
                    int count{0};
                    while (m_waitingWriters != 0 || (initialValue = m_lockCount) < 0)
                    {
                        if (++count % MAX_SPIN_COUNT == 0)
                            std::this_thread::yield();
                    }
                    computedValue = initialValue + 1;
                }
                while (!m_lockCount.compare_exchange_strong(initialValue, computedValue));
            }

            void ReleaseReaderLock()
            {
                assert(m_lockCount > 0);
                --m_lockCount;
            }

            void AcquireWriterLock()
            {
                int computedValue, initialValue;
                ++m_waitingWriters;
                do
                {
                    int count{0};
                    while (m_lockCount != 0)
                    {
                        if (++count % MAX_SPIN_COUNT == 0)
                            std::this_thread::yield();
                    }
                    initialValue = 0;
                    computedValue = -1;
                }
                while (!m_lockCount.compare_exchange_strong(initialValue, computedValue));
                --m_waitingWriters;
            }

            void ReleaseWriterLock()
            {
                assert(m_lockCount == -1);
                ++m_lockCount;
            }

        private:
            static constexpr int MAX_SPIN_COUNT = 1000;
            std::atomic m_lockCount{0};
            std::atomic m_waitingWriters{0};
        };
    }
}

说明:负m_lockCount表示活动写入器(因为一次只能有一个写入器,最小值为-1),正计数表示活动读取器。当查询读取器锁时,它会旋转(并生成每个MAX_SPIN_COUNT自旋),直到没有等待的写入器(因为它应该支持这些作者),并且没有活动的写入器(m_lockCount >= 0),然后它尝试更新锁计数并在失败时重复该进程。释放锁时,无需进一步检查,锁计数就会减少。写入器锁的方法与暂时递增m_waitingWriters而不是在获取方法中检查它们的方法非常相似。

我知道您通常应该依赖库函数,但在我的测试中,获得写入器锁的速度是boost::shared_mutex的10倍,对于D5(四个阅读器试图在一个循环中获取读取器锁)速度是boost::shared_lockboost::unique_lock的两倍。所以这是值得的。

同样,在我的应用程序中,线程计数等于处理器计数,因此很少需要产生结果,而自旋锁只用于只需要几个周期的方法。

我还想显式地删除移动和复制构造函数,因为这对这样的类是有意义的(尽管它们因为原子字段而被隐式删除)。这是个好主意吗

EN

回答 1

Code Review用户

回答已采纳

发布于 2019-02-19 19:32:33

On Threading

无锁代码很少容易理解。

我不敢苟同。它通常只是隐藏了复杂的情况。

On Spin Locks.

自旋锁通常不是一个好主意。您应该确信,陷入自旋锁中的线程将快速逃脱(否则您将熔化您的处理器)。

但是您可以通过使用std::this_thread::yield()来缓解这个问题,所以这不是一个很大的问题。所以这不是我所说的经典的自旋锁。更多的是随着屈服而旋转。

保证逃逸

我看到的唯一问题是,对于某个特定线程,您不能保证锁的逸出。

一些特殊的不幸线程在试图获得锁时可能会被捕获,而其他线程则会通过并继续获取锁,从而迫使不幸的线程继续尝试退出获取。

因此,您的自旋锁有可能导致某些资源饥饿(或者最坏的情况是编写代码序列)。

这通常是通过维持秩序来实现的。

Documentation

您需要将解释放入代码中(作为注释)。把它作为这个网站的一个解释是很好的,并允许我理解代码。但如果没有它,代码就无法解密。

RAII

您已经设计了需要匹配方法调用的代码(这是错误的做法)。此外,您还没有为这个类显示RAII储物柜,所以我们必须假定它的使用并不是例外的安全。

您的所有四种方法:

代码语言:javascript
复制
        void AcquireReaderLock()
        void ReleaseReaderLock()
        void AcquireWriterLock()
        void ReleaseWriterLock()

应该是私人成员。您应该只允许通过RAII锁保护来访问这些方法。以std::lock为例。

最佳实践

轻微的最佳做法违规行为:

代码语言:javascript
复制
// One declaration per line
int computedValue, initialValue;

// Why not initialize these on declaration.
initialValue = 0;
computedValue = -1;
// There is no need to ever set them again.
票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/213823

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档