我必须要有一个时刻,因为这应该很容易,但我似乎无法让它正常工作.
什么是在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++标准)和容易打破.我建议使用一个简单的互斥锁/锁,除非你对它进行了分析并发现它是一个瓶颈.