是否将指针视为C中的原子动作?

时间:2009-05-18 18:17:47

标签: c multithreading synchronization

如果我有一个多线程程序,它通过引用读取缓存类型的内存。我可以通过主线程更改此指针,而不会冒任何其他线程读取意外值的风险。

正如我所看到的,如果更改是原子的,其他线程将读取旧值或更新值;从不随机内存(或空指针),对吗?

我知道无论如何我应该使用同步方法,但我仍然很好奇。

指针是否变为原子?

更新:我的平台是64位Linux(2.6.29),虽然我也想要一个跨平台的答案:)

7 个答案:

答案 0 :(得分:24)

正如其他人所提到的,C语言中没有任何内容可以保证这一点,并且它取决于您的平台。

在大多数现代桌面平台上,对字大小的对齐位置的读/写将是原子的。但由于处理器和编译器对读写的重新排序,这确实无法解决您的问题。

例如,以下代码已损坏:

主题A:

DoWork();
workDone = 1;

主题B:

while(workDone != 0);

ReceiveResultsOfWork();

尽管对workDone的写入是原子的,但在许多系统上,处理器无法保证在通过workDone进行写入之前,对DoWork()的写入对其他处理器是可见的。可见。在调用workDone之前,编译器也可以自由地将写入重新排序到DoWork()。在这两种情况下,ReceiveResultsOfWork()可能会开始处理不完整的数据。

根据您的平台,您可能需要插入内存挡板等,以确保正确的订购。为了做到这一点,这可能非常棘手。

或者只是使用锁。更简单,更容易验证是正确的,并且在大多数情况下足够高性能。

答案 1 :(得分:12)

C语言没有说明任何操作是否是原子的。我曾经研究过8位总线和16位指针的微控制器;这些系统上的任何指针操作都可能是非原子的。我想我记得英特尔386s(其中一些有16位总线)引起了类似的担忧。同样,我可以想象具有64位CPU但32位数据总线的系统,这可能会引起与非原子指针操作类似的担忧。 (我没有检查是否确实存在任何此类系统。)

编辑:Michael's answer非常值得一读。总线大小与指针大小不是关于原子性的唯一考虑因素;这只是我想到的第一个反例。

答案 2 :(得分:5)

你没有提到一个平台。所以我认为一个稍微准确的问题是

  

指针变化是否保证是原子的?

区别是必要的,因为不同的C / C ++实现可能会在此行为中有所不同。特定平台可以保证原子分配并且仍然在标准范围内。

至于在C / C ++中是否总体上保证这一点,答案是否定的.C标准没有这样的保证。保证指针赋值的唯一方法是使用特定于平台的机制来保证赋值的原子性。例如,Win32中的Interlocked方法将提供此保证。

你在做哪个平台?

答案 3 :(得分:4)

问题的答案是C规范不要求指针赋值为原子,因此你不能指望它是原子的。

实际答案可能是它可能取决于您的平台,编译器,以及编写程序当天星星的对齐情况。

答案 4 :(得分:3)

'普通'指针修改不保证是原子的。

检查'比较和交换'(CAS)和其他原子操作,而不是C标准,但大多数编译器都可以访问处理器原语。在GNU gcc案例中,有几个built-in functions

答案 5 :(得分:1)

标准唯一保证的是sig_atomic_t类型。

正如您从其他答案中看到的那样,在针对通用x86架构时可能会出现问题,但使用更多“专业”硬件则风险很大。

如果你真的很想知道,你可以将sizeof(sig_atomic_t)与sizeof(int *)进行比较,看看它们是你的目标系统。

答案 6 :(得分:1)

事实证明这是一个非常复杂的问题。我问了similar question并阅读了我指向的所有内容。我学到了很多关于缓存如何在现代架构中工作的知识,并没有找到任何明确的东西。正如其他人所说,如果总线宽度小于指针位宽,则可能会遇到麻烦。特别是如果数据跨越缓存行边界。

谨慎的架构将使用锁。