我有一个多线程应用程序,其中我有一个生产者线程(主)和多个消费者。
现在,从主要方面来说,我希望在消费者的工作范围内有一定比例。实现计数器很容易,因为循环完成的工作。然而,由于这个循环重复了几千次,甚至可能超过一百万次。我不想互斥这个部分。所以我开始研究写入int的一些原子选项。
据我所知,我可以使用gcc的内置原子函数: https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html
但是,它没有只读取我想要处理的变量的功能。
基本上我的问题是。
或
答案 0 :(得分:3)
定义“安全”。
如果您只是在x86上使用常规读取,对于自然对齐的32位或更小的数据,读取是原子的,因此您将始终读取有效值,而不是包含由一个线程写入的某些字节的值,而是一些另一个。如果这些事情中的任何一个都不正确(不是x86,不是自然对齐,大于32位......),所有投注都会关闭。
尽管如此,您无法保证读取的值特别新鲜,或者在多次读取中看到的值序列将以任何特定顺序排列。我已经看到使用volatile
的天真代码来打败编译器完全优化读取但没有其他同步机制,实际上从来没有看到由于CPU缓存而更新的值。
如果这些事情中的任何一个对您很重要,而且他们确实应该这样做,那么您应该明确地使读取原子并使用适当的内存屏障。你引用的内在函数为你处理这两件事:你可以调用一个原子内在函数,除了返回值之外没有任何副作用:
__sync_val_compare_and_swap(ptr, 0, 0)
或
__sync_add_and_fetch(ptr, 0)
或
__sync_sub_and_fetch(ptr, 0)
或其他什么
答案 1 :(得分:2)
如果您的编译器支持它,您可以使用C11原子类型。它们在标准的第7.17节中介绍,但遗憾的是它们是可选的,因此如果不支持__STDC_NO_ATOMICS__
,则必须检查gcc
是否定义为至少抛出有意义的错误。
使用{{1}},您显然至少需要版本4.9,否则标题丢失(here is a SO question about this, but I can't verify because I don't have GCC-4.9)。
答案 2 :(得分:1)
我会回答你的问题,但你应该事先知道原子并不便宜。每次使用原子时,CPU都必须在内核之间进行同步,如果在紧密循环中使用原子,则不会喜欢性能结果。
您链接的页面列出了 writer 的原子操作,但没有说明应该如何读取这些变量。答案是您的其他CPU内核将“看到”更新的值,但您的编译器可能会将旧值“缓存”在寄存器或堆栈中。为防止出现这种情况,我建议您声明变量volatile
以强制编译器不要缓存旧值。
您将遇到的唯一安全问题是陈旧数据,如上所述。
如果你试图用原子做更复杂的事情,你可能遇到一个细微和随机的问题,一个线程写的命令原子与你在另一个线程中看到这些改变的命令。遗憾的是,您没有使用内置语言功能,并且编译器内置设计并不完美。如果您选择使用这些内置函数,我建议您保持逻辑非常简单。
答案 3 :(得分:-1)
如果我理解了这个问题,我就不会对计数器使用任何原子变量。每个工作线程可以有一个单独的计数器,它在本地更新,主线程可以读取整个计数器阵列的近似快照值,因此这成为1个消费者1生产者问题。通过使用__sync_synchronize()或类似内容,可以使主线程对内存可见,例如每5秒一次。