读取一个不locking的并发修改的整数variables是否安全?

假设我在一个类中有一个整型variables,这个variables可能会被其他线程同时修改。 写入受互斥体保护。 我还需要保护阅读吗? 我听说有一些硬件体系结构,如果一个线程修改一个variables,另一个线程读取它,那么读取的结果将是垃圾; 在这种情况下,我确实需要保护读取。 我从来没有见过这样的体系结构。

这个问题假定单个事务只包含更新一个整型variables,所以我不担心任何其他variables的状态也可能涉及到一个事务。

primefaces阅读
如前所述,这是平台的依赖。 在x86上,该值必须在4字节边界上alignment。 通常对于大多数平台,读取必须在单个CPU指令中执行。

优化器caching
优化器不知道你正在读取由不同线程修改的值。 声明值的volatile有助于:优化器将为每个访问发出一个内存读/写,而不是试图将该值保存在寄存器中。

CPUcaching
不过,您可能会读取过时的值,因为在现代体系结构中,您有多个核心,并且个别caching不会自动保持同步。 您需要读取内存屏障,通常是平台特定的指令。

在Wintel上,线程同步function将自动添加一个完整的内存屏障,或者您可以使用InterlockedXxxxfunction。

MSDN: 内存和同步问题 , MemoryBarriermacros

请参阅drhirsch的评论。

你问一个关于读一个variables的问题,稍后你会谈到更新一个variables,这意味着一个读 – 修改 – 写操作。

假设你真的指的是前者,那么读取是安全的, 如果它是一个primefaces操作 。 对于几乎所有的体系结构来说,整数都是如此。

有几个(和罕见的)例外:

  • 读取未alignment,例如在奇数地址访问4字节的int。 通常你需要用特殊的属性强制编译器做一些错位。
  • int的大小大于指令的自然大小,例如在8位体系结构中使用16位整数。
  • 一些架构有一个人为限制的总线宽度。 我只知道很老旧的,像386sx或68008。

在这种情况下, 我build议不要依赖任何编译器或体系结构
每当你有读者和作者的混合(而不是只是读者或作家),你最好同步他们。 想象一下,你的代码运行某人的人造心脏,你不是真的想要读取错误的值,当然你不希望你的城市的发电厂去“boooom”,因为有人决定不使用这个互斥体。 长时间保存自己的睡眠,同步“。
如果你只有一个线程阅读 – 你只需要一个互斥体就行了,但是如果你打算为多个阅读器和多个作者,你需要一个复杂的代码段来同步。 读写locking的一个很好的实现也是“公平的”,我还没有看到。

想象一下,你正在读取一个线程中的variables,该线程在读取时被中断,variables被写入线程改变。 读取线程恢复后,读取整数的值是多less?

除非读取一个variables是一个primefaces操作,在这种情况下,只需要一个(汇编)指令,就不能保证上述情况不会发生。 (variables可以写入内存,检索值将需要多个指令)

一致意见是你应该封装/locking所有的写操作,而读操作可以同时执行(仅)其他的读操作

假设我在一个类中有一个整型variables,这个variables可能会被其他线程同时修改。 写入受互斥体保护。 我还需要保护阅读吗? 我听说有一些硬件体系结构,如果一个线程修改一个variables,另一个线程读取它,那么读取的结果将是垃圾; 在这种情况下,我确实需要保护读取。 我从来没有见过这样的体系结构。

在一般情况下,这可能是每个架构。 每个架构都有与写入同时进行的读取会导致垃圾的情况。 但是,几乎每个架构也有这个规则的例外。

通常情况下,字大小的variables是primefaces读取和写入的,所以在读取写入时不需要同步。 正确的值将被作为一个单独的操作primefaces地写入,并且线程也会将当前值作为单个primefaces操作读取,即使另一个线程正在写入也是如此。 所以对于整数,你在大多数架构上都是安全的。 有些将这个保证延伸到其他几个尺寸,但这显然是硬件依赖的。

对于非字大小的variables,读写通常都是非primefaces的,必须通过其他方式进行同步。

如果在写入新的时候不使用这个variables的前缀值,那么:

您可以读写整数variables而不使用互斥锁。 这是因为整数是32位体系结构中的基本types,每一次修改/读取值都是一次操作。

但是,如果你捐赠诸如增量的东西:

 myvar++; 

那么你需要使用互斥锁,因为这个构造被扩展到myvar = myvar + 1之间,并且在读myvar和增加myvar之间,myvar可以被修改。 在这种情况下,你会得到不好的价值。

尽pipe在没有同步的情况下读取32位系统的内容可能是安全的。 我不会冒这个险。 虽然多个并发读取不是一个问题,但是我不喜欢写入与读取同时发生。

我build议将读取放在关键部分,然后在多个核心上压力testing您的应用程序,看看是否会造成太多的争用。 发现并发错误是我更喜欢避免的噩梦。 如果将来有人决定将整数变为长整数还是双整数,那么会发生什么呢?

如果你有一个漂亮的线程库像boost.thread或zthread那么你应该有读/写锁。 这些将是您的情况的理想,因为他们允许多个读取,同时保护写入。

这可能发生在使用16位整数的8位系统上。

如果你想避免locking,你可以在适当的情况下多次阅读,直到你得到两个相等的连续值。 例如,我使用这种方法来读取32位embedded式目标上的64位时钟,其中时钟节拍是作为中断例程实现的。 在这种情况下,读取三次就足够了,因为时钟只能在短时间内读取例程运行一次。

对具有并发性的variables的读/写操作都必须由临界区(而不是互斥体)保护。 除非你想浪费你整天的debugging时间。

我相信关键部分是特定于平台的。 在Win32上,临界区是非常有效的:当没有联锁发生时,进入临界区几乎是免费的,不影响整体性能。 当互锁发生时,它比互斥体效率更高,因为它在挂起线程之前执行一系列检查。

一般来说,每个机器指令在执行时都要经过几个硬件阶段。 由于大多数当前的CPU是多核或超线程的,这意味着读取一个variables可能会启动它移动通过指令pipe道,但它不会阻止另一个CPU内核或超线程同时执行存储指令到相同地址。 两个同时执行的指令(读取和存储)可能“跨越path”,这意味着读取将在新值被存储之前接收到旧值。
恢复:你需要读写互斥。

取决于你的平台。 大多数现代平台提供整数的primefaces操作:Windows有InterlockedIncrement,InterlockedDecrement,InterlockedCompareExchange等。这些操作通常由底层硬件支持(读取:CPU),并且通常比使用临界区或其他同步机制便宜。

请参阅MSDN: InterlockedCompareExchange

我相信Linux(和现代Unix变体)在pthreads包中支持类似的操作,但是我并不认为它是一个专家。

如果一个variables被标记为volatile关键字,那么读/写就会变成primefaces,但是这对于编译器的行为和行为有很多其他的影响,不应该仅仅用于这个目的。

在你盲目地开始使用它之前,请先阅读一下volatile的含义: http : //msdn.microsoft.com/en-us/library/12a04hfd(VS.80).aspx