gcc中的原子计数器

时间:2010-11-12 03:56:47

标签: 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;
}

但它从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;
 }

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

4 个答案:

答案 0 :(得分:13)

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

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

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

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

return (unsigned) __sync_fetch_and_add(&count, 1) % (unsigned) limit;

或者

return __sync_fetch_and_add(&count, 1) & (limit - 1);

每次调用只执行一次原子操作,安全且非常便宜。对于通用限制,您仍然可以使用%,但如果计数器溢出,则会破坏序列。您可以尝试使用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));

这种方法也可以推广到更复杂的序列和更新操作。

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

答案 1 :(得分:2)

你很幸运,因为你想要的范围恰好恰好适合2位。

简单的解决方案:让volatile变量永远计数。但在阅读之后,只使用最低的两位(val & 3)。 Presto,0-3的原子计数器。

答案 2 :(得分:0)

即使使用volatile,也无法在纯C中创建任何原子。你需要asm。 C1x将具有特殊的原子类型,但在那之前你会遇到asm。

答案 3 :(得分:0)

你有两个问题。

__sync_fetch_and_add将返回 之前的 值(即在添加之前)。因此,在_count变为3的步骤中,您的本地save_count变量将返回2。因此,您实际上必须先将_count增加到4,然后再将其作为3返回。

但即使最重要的是,在将其重置为0之前,您还是专门寻找它>= 4。如果您只是寻找它,那只是使用错误限制的问题高达三。