保证多处理器系统上的多个线程始终可以看到变量的最新值

我正在使用这样的配置:

  • .NET framework 4.5
  • Windows Server 2008 R2
  • HP DL360p Gen8(2 * Xeon E5-2640,x64)

我的程序中有这样的字段:

protected int HedgeVolume; 

我从几个线程访问此字段。 我假设因为我有多处理器系统,所以这些线程可能在不同的处理器上执行。

我该怎么做才能保证我在任何时候使用这个字段时最近的值是“读”? 并确保当我“写”值时,它立即可用于所有其他线程?

我该怎么办?

  • 只是按原样离开现场。
  • 声明它volatile
  • 使用Interlocked类访问该字段
  • 使用.NET 4.5 Volatile.ReadVolatile.Write方法访问该字段
  • 使用lock

我只需要最简单的方法让我的程序在这个配置上工作我不需要我的程序在另一台计算机或服务器或操作系统上工作。 此外,我想要最小的延迟,所以我正在寻找最快的解决方案,它将始终适用于此标准配置(多处理器intel x64,.net 4.5)。

您的问题缺少一个关键要素……该领域数据的完整性有多重要?

volatile会为您提供性能,但如果某个线程当前正在对该字段进行更改,那么在完成之前您将无法获取该数据,因此您可能会访问过期信息,并可能覆盖另一个线程当前正在执行的更改。 如果数据敏感,您可能会收到很难跟踪的错误。 但是,如果您正在进行非常快速的更新,请在不读取值的情况下覆盖该值,并且不要忘记曾经有一段时间(过了几毫秒)数据,请继续进行更新。

lock担保,一次只有一个线程可以访问该字段。 您只能将其放在编写字段的方法上,并单独保留读取方法。 缺点是,它很慢,并且可能在另一个执行其任务时阻塞线程。 但是,您确定您的数据保持有效。

存在Interlock以保护自己免受调度程序上下文切换的影响。 我的看法? 除非您确切知道为什么要使用它以及确切如何使用它,否则不要使用它。 它提供了选择,但是有很多选择会带来很大的问题。 它会在更新变量时阻止上下文切换。 它可能不会按照您的想法执行操作,也不会阻止并行线程同时执行其任务。

您想使用Volatile.Read()

当您在x86上运行时,C#中的所有写操作都等同于Volatile.Write() ,您只需将此用于Itanium。

Volatile.Read()将确保您获得最新的副本,无论上次写入哪个线程。

这里有一个很棒的写作, C#Memory Model Explained

摘要包括,

在某些处理器上,编译器不仅必须避免对易失性读写进行某些优化,还必须使用特殊指令。 在多核计算机上,不同的核心具有不同的高速缓存。 处理器可能不打算默认保持这些高速缓存一致,并且可能需要特殊指令来刷新和刷新高速缓存。

希望这很明显,除了需要volatile来阻止编译器优化它之外,还有处理器。

但是,在C#中,无论您是写入易失性字段还是非易失性字段,所有写入都是易失性的(与Java中不同)。 所以,上述情况实际上从未在C#中发生过。 易失性写入更新线程的缓存,然后将整个缓存刷新到主存储器。

你不需要Volatile.Write() 。 这里有更多的authorratitive源, Joe Duffy CLR Memory Model 。 但是,您可能需要它来停止编译器重新排序它。

由于所有C#写入都是易失性的,因此您可以将所有写入视为直接进入主存储器。 常规的非易失性读取可以从线程的缓存中读取值,而不是从main读取

你需要Volatile.Read()

当您开始设计并发程序时,您应该按优先顺序考虑这些选项:

1)隔离:每个线程都有自己的私有数据
2)不变性:线程可以看到共享状态,但它永远不会改变
3)可变共享状态:使用锁保护对共享状态的所有访问

如果你到达(3),那么你实际需要多快?

获得无争议的锁定大约需要10ns(10 -8秒) – 这对于大多数应用来说足够快,并且是保证正确性的最简单方法。

使用你提到的任何其他选项都会带你进入低锁编程领域,这很难弄清楚。


如果您想学习如何编写并发软件,您应该阅读以下内容:

简介: Joe Albahari的免费电子书 – 大约需要一天时间阅读

圣经: Joe Duffy的“Windows上的并发编程” – 将花费大约一个月的时间来阅读

取决于你做了什么。 对于只读,易挥发是最简单的,互锁允许更多的控制。 锁定是不必要的,因为它比你描述的问题更加琐碎。 不确定Volatile.Read/Write,从未使用它们。

volatile – 糟糕,有一些问题(参见Joe Duffy的博客)

如果你所做的只是读取值或无条件写一个值 – 使用Volatile.ReadVolatile.Write

如果您需要阅读并随后写入更新的值 – 请使用lock语法。 但是,您可以使用Interlocked类function实现相同的效果,但这更复杂(包括CompareExchange以确保您正在更新读取值,即自读取操作后未进行修改+逻辑CompareExchange ,如果值为自阅读后修改)。

从中我可以理解,您希望能够读取它在字段中写入的最后一个值。 让我们对数据的sql concurency问题进行类比。 如果您希望能够读取字段的最后一个值,则必须进行primefaces指令。 如果某人正在编写字段,则必须锁定所有线程才能读取,直到该线程完成写入事务。 之后,该线程上的每次读取都是安全的。 问题不在于阅读和写作一样。 如果你问我的话,只要它的书写足够就锁定该字段……

首先来看看: Volatile vs. Interlocked vs. lock

对于多内核cpu来说,volatile修饰符是一个很好的选择。

但这够了吗? 这取决于你如何计算新的HedgeVolume值!

  • 如果你的新HedgeVolume不依赖于当前的HedgeVolume,那么你就完成了volatile。

  • 但是如果HedgeVolume [x] = f(HedgeVolume [x-1])那么你需要一些线程同步来保证在计算和分配新值时HedgeVolume不会改变。 锁定和互锁szenarios都适用于这种情况。

我有一个类似的问题,发现这篇文章非常有帮助。 这是一个很长的阅读,但我学到了很多东西!