使用atomic_flag旋转锁进行内存排序



我正在努力熟悉c++11的新内存排序概念,并相信我实际上已经很好地掌握了这些概念,直到我偶然发现了这个旋转锁的实现:

#include <atomic>
namespace JayZ
{
    namespace Tools
    {
        class SpinLock
        {
        private:
            std::atomic_flag spin_lock;
        public:
            inline SpinLock( void ) : atomic_flag( ATOMIC_FLAG_INIT ) {}
            inline void lock( void )
            {
                while( spin_lock.test_and_set( std::memory_order_acquire ) )
                    ;
            }
            inline void unlock( void )
            {
                lock.clear( std::memory_order_release );
            }
        };
    }
}

例如,在http://en.cppreference.com/w/cpp/atomic/atomic_flag
以及《行动中的并发》一书。我还在SO.的某个地方找到了它

但我就是不明白为什么它会起作用
假设线程1调用lock(),test_and_set()返回0,因为旧值-->线程1已经获取了锁
但随后线程2出现并尝试相同的操作。现在,由于没有发生"存储同步"(release,seq_cst_acq_rel),线程1的存储到spin_lock的类型应该是松弛的
但由此可知,它不能与线程2读取spin_lock同步。这样线程2就可以从spin_lock中读取值0,从而也可以获取锁
我的错误在哪里?

您的错误在于忘记了spin_lockatomic_flag,因此test_and_set是原子操作。需要memory_order_acquirememory_order_release来防止在锁定操作之前读取迁移到或在解锁之后写入迁移到。锁本身受到原子性的保护,原子性总是包括可见性。

对于给定的原子变量,有一个"修改顺序"。一旦线程1 test_and_将值从0设置为1,线程2就不可能看到0。

内存顺序会影响所有其他内存地址的"同步"方式如果一个线程用memory-order_release修改原子变量,那么任何用memory_order_aquire读取同一变量的线程都会"看到"第一个线程在释放前所做的每一次内存更改。

获取和释放与原子无关。这是关于确保每个成功锁定spinlock的线程"看到"以前锁定它的每个线程的变化。

修改顺序是使算法无锁定的关键。线程1和线程2都试图对同一个变量进行test_and_set,因此根据规则,一个修改"先于"另一个修改。因为test_and_set"发生在"另一个线程"进展之前",所以至少有一个线程必须始终取得进展。这是无锁定的定义

原子标志上的test_and_set操作被指定为具有特殊特性的读-修改-写操作,其中之一是:

原子读取-修改-写入操作应始终读取与读取-修改写入操作相关联的写入之前写入的最后一个值(按修改顺序)。[n3337§29.3/12]

例如,这也是fetch_add工作的原因,而读取修改顺序中的最新值不需要简单的加载操作。

相关内容

  • 没有找到相关文章

最新更新