在C#下使用Int64会对32位处理器造成危险

我在MS文档中读到,在32位Intel计算机上分配64位值不是primefaces操作; 也就是说,操作不是线程安全的。 这意味着如果两个人同时为静态Int64字段分配值,则无法预测该字段的最终值。

三部分问题:

  • 这是真的吗?
  • 这是我在现实世界中会担心的吗?
  • 如果我的应用程序是multithreading的,我真的需要用锁定代码包围我的所有Int64分配吗?

这不是您遇到的每个变量。 如果某个变量用作共享状态或某些东西(包括但不限于某些 static字段),则应该注意这个问题。 由于在闭包或迭代器转换中被关闭而未被提升的局部变量完全没有问题,并且一次由单个函数(因此,单个线程)使用。

即使写入primefaces的,每当您访问变量时,您仍然需要取出锁定。 如果你不这样做,你至少必须使变量volatile变为确保所有线程在下次读取变量时看到新值(这几乎总是你想要的)。 这可以让你做primefaces的,易变的集合 – 但是只要你想做更有趣的事情,比如给它添加5,你就会回到锁定状态。

无锁编程是非常非常难以正确的。 您需要确切地知道自己在做什么,并将复杂性保持在尽可能小的代码段。 就个人而言,除了非常着名的模式之外,我甚至很少尝试尝试它,例如使用静态初始化程序来初始化集合,然后在没有锁定的情况下从集合中读取。

使用Interlocked类可以在某些情况下提供帮助,但是锁定它几乎总是容易多了。 无可争议的锁是“非常便宜的”(不可否认,它们随着更多内核而变得昂贵,但所有内容也是如此) – 在您有充分的证据certificate它实际上会产生重大影响之前,不要乱用无锁代码。

MSDN :

分配此类型的实例在所有硬件平台上都不是线程安全的,因为该实例的二进制表示可能太大而无法在单个primefaces操作中分配。

但是也:

与任何其他类型一样,必须通过锁保护对包含此类实例的共享变量的读写,以保证线程安全。

如果你有共享变量(比如,作为类的静态字段,或者作为共享对象的字段),并且该字段或对象将被跨线程使用,那么,是的,你需要确保通过primefaces操作保护对该变量的访问。 x86处理器具有内在函数以确保发生这种情况,并且此工具通过System.Threading.Interlocked类方法公开。

例如:

 class Program { public static Int64 UnsafeSharedData; public static Int64 SafeSharedData; static void Main(string[] args) { Action unsafeAdd = i => { UnsafeSharedData += i; }; Action unsafeSubtract = i => { UnsafeSharedData -= i; }; Action safeAdd = i => Interlocked.Add(ref SafeSharedData, i); Action safeSubtract = i => Interlocked.Add(ref SafeSharedData, -i); WaitHandle[] waitHandles = new[] { new ManualResetEvent(false), new ManualResetEvent(false), new ManualResetEvent(false), new ManualResetEvent(false)}; Action, Object> compute = (a, e) => { for (Int32 i = 1; i <= 1000000; i++) { a(i); Thread.Sleep(0); } ((ManualResetEvent) e).Set(); }; ThreadPool.QueueUserWorkItem(o => compute(unsafeAdd, o), waitHandles[0]); ThreadPool.QueueUserWorkItem(o => compute(unsafeSubtract, o), waitHandles[1]); ThreadPool.QueueUserWorkItem(o => compute(safeAdd, o), waitHandles[2]); ThreadPool.QueueUserWorkItem(o => compute(safeSubtract, o), waitHandles[3]); WaitHandle.WaitAll(waitHandles); Debug.WriteLine("Unsafe: " + UnsafeSharedData); Debug.WriteLine("Safe: " + SafeSharedData); } } 

结果:

不安全 :-24050275641 安全 :0

有趣的是,我在Vista 64上以x64模式运行它。这表明64位字段被运行时视为32位字段,即64位操作是非primefaces的。 有人知道这是CLR问题还是x64问题?

在32位x86平台上,最大的primefaces大小的内存是32位。

这意味着如果某些内容写入或读取64位大小的变量,则该读/写操作可能会在执行期间被抢占。

  • 例如,您开始为64位变量赋值。
  • 写入前32位后,操作系统决定另一个进程将获得CPU时间。
  • 下一个进程尝试读取您在分配时的变量。

这只是一个可能的竞争条件,在32位平台上进行64位分配。

但是,即使使用32位变量,也可能存在读写条件,因此任何共享变量都应以某种方式同步以解决这些竞争条件。

这是真的吗? 是的,事实certificate。 如果您的寄存器中只有32位,并且您需要将64位值存储到某个存储器位置,则需要进行两次加载操作和两次存储操作。 如果您的进程被这两个加载/存储之间的另一个进程中断,则另一个进程可能会损坏您的一半数据! 奇怪但真实。 这在每个构建的处理器上都存在问题 – 如果您的数据类型比寄存器长,则会出现并发问题。

这是我在现实世界中会担心的吗? 是的,不是。 由于几乎所有现代编程都有自己的地址空间,因此如果您正在进行multithreading编程,则只需要担心这一点。

如果我的应用程序是multithreading的,我真的需要用锁定代码包围我的所有Int64分配吗? 可悲的是,是的,如果你想获得技术支持。 实际上,在较大的代码块周围使用Mutex或Semaphore比在全局可访问的变量上锁定每个单独的set语句通常更容易。