gcc中的原子计数器

Mat*_*att 11 c++ gcc

我必须要有一个时刻,因为这应该很容易,但我似乎无法让它正常工作.

什么是在GCC中实施原子计数器的正确方法?

即我想要一个从0到4运行的计数器并且是线程安全的.

我这样做(这是进一步包装在一个类,但不是在这里)

static volatile int _count = 0;
const int limit = 4;

int get_count(){
  // Create a local copy of diskid
  int save_count = __sync_fetch_and_add(&_count, 1);
  if (save_count >= limit){
      __sync_fetch_and_and(&_count, 0); // Set it back to zero
  }
  return save_count;
}
Run Code Online (Sandbox Code Playgroud)

但它从1到4(包括1和4),然后从大约到零.
它应该从0到3.通常我会使用mod运算符进行计数器,但我不知道如何安全地执行此操作.

也许这个版本更好.你能看到它的任何问题,或提供更好的解决方案.

int get_count(){
   // Create a local copy of diskid
   int save_count = _count;
   if (save_count >= limit){
      __sync_fetch_and_and(&_count, 0); // Set it back to zero
      return 0;
   }

   return save_count;
 }
Run Code Online (Sandbox Code Playgroud)

实际上,我应该指出,每个线程获得不同的值并不是绝对关键的.如果两个线程碰巧同时读取相同的值,则不会出现问题.但他们随时都不能超过限制.

Fab*_*sen 13

你的代码不是原子的(你的第二个get_count甚至不会增加计数器值)!

Say count在开始时为3,两个线程同时调用get_count.其中一个将首先完成原子添加并count增加到4.如果第二个线程足够快,它可以5在第一个线程将其重置为零之前将其递增.

此外,在您的回绕处理中,您重置count为0但不是save_count.这显然不是预期的.

如果limit是2的幂,这是最简单的.不要自己做减少,只需使用

return (unsigned) __sync_fetch_and_add(&count, 1) % (unsigned) limit;
Run Code Online (Sandbox Code Playgroud)

或者

return __sync_fetch_and_add(&count, 1) & (limit - 1);
Run Code Online (Sandbox Code Playgroud)

这只是每次调用一次原子操作,安全且非常便宜.对于通用限制,您仍然可以使用%,但如果计数器溢出,则会破坏序列.您可以尝试使用64位值(如果您的平台支持64位原子),并希望它永远不会溢出; 不过这是一个坏主意.正确的方法是使用原子比较交换操作.你做这个:

int old_count, new_count;
do {
  old_count = count;
  new_count = old_count + 1;
  if (new_count >= limit) new_count = 0; // or use %
} while (!__sync_bool_compare_and_swap(&count, old_count, new_count));
Run Code Online (Sandbox Code Playgroud)

该方法也推广到更复杂的序列和更新操作.

也就是说,这种类型的无锁操作很难做到,在某种程度上依赖于未定义的行为(所有当前的编译器都是正确的,但在C++ 0x实际上没有明确定义的内存模型之前没有C/C++标准)和容易打破.我建议使用一个简单的互斥锁/锁,除非你对它进行了分析并发现它是一个瓶颈.