我正在写一个非常简单的原子计数器,比如
class counter
{
/* ... */
int increment()
{
return __sync_add_and_fetch( &counter_, 1 );
}
int decrement()
{
return __sync_sub_and_fetch( &counter_, 1 );
}
bool operator==( int val ) // const
{
return __sync_bool_compare_and_swap( &counter_, val, counter_ );
}
private:
volatile int counter_;
};
我可以在return counter_ == val;
内使用operator==
(更短并且可以取消注释const
限定符)吗?或者我应该使用__sync_bool_compare_and_swap
代替?
编辑:所以,我比开始时更困惑。在阅读了评论和答案后,我认为离开“比较和交换”更为充分。是吗?
编辑2:我摆脱了这个operator==
,但我仍然很好奇。我会等待并希望在这个问题上多做一些活动。感谢。
答案 0 :(得分:4)
是和否。
测试等式是原子的,因为它是线程安全的,但它只检查那个时间点的相等性。只需一秒钟的一个或另一个,你所比较的值可能会有不同的值。特别是,这是不常量函数,因为它使用可能会发生变化的隐藏数据。
那就是说,我会在这里使用==
,因为你的交换看起来毫无意义。
编辑:
重点是变量 load 需要是原子的,而不是任何其他东西。
以下是您的日常工作:
在寄存器中接收一些值。
从内存中加载一个整数。
在寄存器中编辑一些值。
返回。
那里只有一件事与并发有关:负载。计数器的价值可能会不断变化,但你的日常工作不能做任何特别的事情。即使使用锁定,它所能做的最好的事情就是加载当前值并使用它具有的功能。
可能是函数返回时值已经改变,但是所有代码可能希望做的是说它是否与它看起来的值相匹配。 调用者的责任是确保这是一个有效的事情(即在完成之前没有任何东西会更新计数器)。
结构,数组,向量或任何其他多值类型的加载将要求您获取读锁定以确保您获得的是自洽的(您读取的第一个对应于你读的最后一点)。但是一个整数的加载很可能是原子的(尽管不是实际由C / C ++保证),所以你不必担心。
等于运算符==
仅对函数自身上下文(寄存器和堆栈)的内容进行操作,因此没有并发问题(除非您获取了堆栈变量的地址并将其发送到另一个线程)。
答案 1 :(得分:1)
比较2个volatile变量永远不会是无原子的(我认为你的意思是这个),因为你必须每次从内存加载变量而不能在寄存器中处理它们。比较是在大多数架构上通过减去两者并检查零值来完成的。这至少有3条说明。
由于两个值之间存在任何关系,因此只有在做出决定时才需要原子性问题。由于值的波动性,您必须轮询这些值。那么比较的原子性是没有意义的,因为如果你不能在本回合中获得平等,那么你将在下一回合得到它,但是没有保证你会击中每一个平等。所以从这个意义上讲,你与__sync_bool_compare_and_swap
进行比较毫无意义。