C++ std::atomic_标志不提供加载或存储操作有什么缺点吗?(旋转锁示例)
与C++ std::atomic_标志不提供加载或存储操作有什么缺点吗?(旋转锁示例),c++,c++11,atomic,stdatomic,C++,C++11,Atomic,Stdatomic,与std::atomic_bool(aka)相比,在我看来std::atomic_标志只是有一个更简单的接口。它只提供测试+设置和清除标志,而std::atomic_bool也为多个运算符提供重载 我的一个问题是关于术语:“加载或存储操作”是什么意思?这是否意味着不可能任意读取和修改std::atomic_flag的值 此外,我想知道,当用于自旋锁时,std::atomic_bool能更快吗?在我看来,std::atomic_标志在自旋锁期间始终必须读写: while (my_atomic_fl
std::atomic_bool
(aka)相比,在我看来std::atomic_标志
只是有一个更简单的接口。它只提供测试+设置和清除标志,而std::atomic_bool
也为多个运算符提供重载
我的一个问题是关于术语:“加载或存储操作”是什么意思?这是否意味着不可能任意读取和修改std::atomic_flag
的值
此外,我想知道,当用于自旋锁时,std::atomic_bool
能更快吗?在我看来,std::atomic_标志
在自旋锁期间始终必须读写:
while (my_atomic_flag.test_and_set()); // spin-lock
而std::atomic_bool
只需执行读取操作(假设原子bool实现为无锁):
std::atomic_标志
是否比std::atomic_标志
的效率更高,或者它也可能是另一种方式?旋转锁应该使用什么
什么是“加载或存储操作”?这是否意味着无法任意读取和修改std::atomic_标志的值
std::atomic_标志
不支持正常的存储/加载操作它是一个仅修改的类型;也就是说,如果不执行修改操作,就无法读取访问
std::atomic_flag
对象。一般来说,
std::atomic_flag
是用于其他操作的构建块。它的界面非常简单;它是唯一保证原子无锁操作的原子类型。
它支持的操作包括:
std::atomic_flag::clear()
std::atomic_flag::test_and_set()
这样,您就可以轻松构建自己的自旋锁(尽管通常不推荐):
此外,我想知道,当用于自旋锁时,std::atomic_bool会更快吗?在我看来,std::atomic_标志在自旋锁期间总是必须读写
问题是,自旋锁在获取锁时总是要修改它的状态。你不能不告诉别人就拿了锁。基于
std::atomic
的lock()
实现看起来非常相似:
while (flag.exchange(true));
基于std::atomic_标志的自旋锁是否更快?
在我的平台上,编译器为这两种类型发出相同的程序集,所以我会非常惊讶代码>不实现自旋锁,while(my_atomic_flag.test_和_set())代码>是的。啊,你是对的。。。我发布的不是一个旋转锁,而是一个旋转等待(因为我在当前的项目中使用了它)。一个线程必须等待另一个线程完成某些操作。因此,我想,这更像是一种信号量类型的用例。好吧,您可以使用双重检查锁定来实现自旋锁,这种锁定在大多数情况下只执行加载操作,而不是RMW,RMW是std::atomic_flag
的唯一选项。它会产生很大的差异吗?我对此表示怀疑,但仍然如此。@ixSci:我的理解是,通常您希望以只读方式旋转,直到看到可用的锁,然后尝试使用它。然后缓存线可以在所有等待的线程中处于“共享”状态,而不是让它们都充斥着失效请求,以独占地拥有缓存线并将其反弹。这个答案的最后一部分的问题是在atomic::exchange
上旋转,这不是一个好的实现,但是使用atomic\u flag
编写的东西再好不过了。当然,你需要特定于平台的东西来制作一个真正好的自旋锁。e、 g.x86的pause
/\u mm\u pause()
指令,并回退到操作系统辅助的睡眠/唤醒。但我认为一般来说,使用原子(如果总是没有锁的)的手动旋转旋转锁会比使用原子标记@PeterCordes的旋转锁稍微好一些,这正是我的意思。我只是不确定一个或另一个实现在所有情况下都是最好的。例如,使用exchange
时,您将损失较少的比赛,但缓存命中率更高。所以我相信一切都取决于任务。无论如何,如果OP有这样的问题,如果我是他,我就不会编写自定义自旋锁。@ixSci:Hmm,对于SMT(例如,超线程),在xchg
上旋转将更多时间阻塞逻辑核心,允许非等待线程运行更多指令。如果我们没有完全可移植代码中的pause
指令,那么与旋转只读相比,它还可以整体节省功耗。所以,是的,它不像我想象的那样清楚,.exchange()
对于一个完全通用的实现来说更糟糕。我不鼓励任何人使用通用的便携式C++编写自定义自旋锁;真正的是通过指数回退和类似暂停的东西来仔细调整的。
class my_spinlock {
std::atomic_flag flag = ATOMIC_FLAG_INIT;
public:
void lock()
{
while(flag.test_and_set());
}
void unlock()
{
flag.clear();
}
};
while (flag.exchange(true));