gpt4 book ai didi

c++ - gcc 中的原子计数器

转载 作者:可可西里 更新时间:2023-11-01 15:08:07 25 4
gpt4 key购买 nike

我一定是有时间,因为这应该很容易,但我似乎无法让它正常工作。

在 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 到 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;
}

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

最佳答案

您的代码不是原子的(您的第二个 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++ 标准型号)并且很容易折断。我建议使用简单的互斥锁/锁,除非您分析过它并发现它是一个瓶颈。

关于c++ - gcc 中的原子计数器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4161494/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com