【问题标题】:.NET 3.5SP1 64-bit memory model vs. 32-bit memory model.NET 3.5SP1 64 位内存模型与 32 位内存模型
【发布时间】:2011-02-28 19:21:50
【问题描述】:

据我了解,32 位机器上的 .NET 内存模型保证 32 位字的写入和读取是原子操作,但不提供对 64 位字的这种保证。我编写了一个快速工具来演示在 Windows XP 32 位操作系统上的这种效果,并且得到的结果与该内存模型描述一致。

但是,我采用了相同工具的可执行文件并在 Windows 7 Enterprise 64 位操作系统上运行它,得到的结果大相径庭。这两台机器的规格相同,只是安装了不同的操作系统。我原以为.NET 内存模型将保证对 32 位和 64 位字的写入和读取在 64 位操作系统上是原子的。我发现结果与这两个假设完全相反。 32 位读写在这个操作系统上没有被证明是原子的。

有人可以向我解释为什么这在 64 位操作系统上会失败吗?

工具代码:

using System;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var th = new Thread(new ThreadStart(RunThread));
            var th2 = new Thread(new ThreadStart(RunThread));
            int lastRecordedInt = 0;
            long lastRecordedLong = 0L;
            th.Start();
            th2.Start();
            while (!done)
            {
                int newIntValue = intValue;
                long newLongValue = longValue;
                if (lastRecordedInt > newIntValue) Console.WriteLine("BING(int)! {0} > {1}, {2}", lastRecordedInt, newIntValue, (lastRecordedInt - newIntValue));
                if (lastRecordedLong > newLongValue) Console.WriteLine("BING(long)! {0} > {1}, {2}", lastRecordedLong, newLongValue, (lastRecordedLong - newLongValue));
                lastRecordedInt = newIntValue;
                lastRecordedLong = newLongValue;
            }
            th.Join();
            th2.Join();
            Console.WriteLine("{0} =? {2}, {1} =? {3}", intValue, longValue, Int32.MaxValue / 2, (long)Int32.MaxValue + (Int32.MaxValue / 2));
        }

        private static long longValue = Int32.MaxValue;
        private static int intValue;
        private static bool done = false;

        static void RunThread()
        {
            for (int i = 0; i < Int32.MaxValue / 4; ++i)
            {
                ++longValue;
                ++intValue;
            }
            done = true;
        }
    }
}

Windows XP 32 位结果:

Windows XP 32-bit
Intel Core2 Duo P8700 @ 2.53GHz
BING(long)! 2161093208 > 2161092246, 962
BING(long)! 2162448397 > 2161273312, 1175085
BING(long)! 2270110050 > 2270109040, 1010
BING(long)! 2270115061 > 2270110059, 5002
BING(long)! 2558052223 > 2557528157, 524066
BING(long)! 2571660540 > 2571659563, 977
BING(long)! 2646433569 > 2646432557, 1012
BING(long)! 2660841714 > 2660840732, 982
BING(long)! 2661795522 > 2660841715, 953807
BING(long)! 2712855281 > 2712854239, 1042
BING(long)! 2737627472 > 2735210929, 2416543
1025780885 =? 1073741823, 3168207035 =? 3221225470

注意 BING(int) 是如何从不被写入的,并证明 32 位读/写在这个 32 位操作系统上是原子的。

在 Windows 7 Enterprise 64 位上的结果:

Windows 7 Enterprise 64-bit
Intel Core2 Duo P8700 @ 2.53GHz
BING(long)! 2208482159 > 2208121217, 360942
BING(int)! 280292777 > 279704627, 588150
BING(int)! 308158865 > 308131694, 27171
BING(long)! 2549116628 > 2548884894, 231734
BING(int)! 534815527 > 534708027, 107500
BING(int)! 545113548 > 544270063, 843485
BING(long)! 2710030799 > 2709941968, 88831
BING(int)! 668662394 > 667539649, 1122745
1006355562 =? 1073741823, 3154727581 =? 3221225470

注意 BING(long) 和 BING(int) 都显示了!为什么 32 位操作失败,更不用说 64 位操作了?

【问题讨论】:

  • 您是否可以编辑您的问题以使用工具栏上的“代码示例”格式?阅读起来非常困难..
  • 我的格式正确,但最初使用的是 TAB 字符而不是空格,并且预览看起来很棒。当我发现它在发布时完全失败时,我感到震惊。我现在修好了。
  • 你能把longValueintValue标记为volatile吗?这将消除任何内存障碍问题,而不会影响您尝试测试的行为。
  • longs 不能被标记为 volatile。

标签: c# .net 64-bit 32-bit


【解决方案1】:

在您的线程回调中,您所做的不仅仅是写或读:

++longValue;
++intValue;

不能保证读写都是原子的。使用Interlocked.Increment 保证此操作的原子性。

【讨论】:

  • 我不希望这些行是明确的原子。我希望内存模型保证写操作是原子的。这就是我想要证明的。显然我应该使用 Interlocked.Increment(ref intValue or longValue) 但这会破坏演示的目的。
  • 写操作是原子的,正如达林所说,它是读+写,而不是。因此,如果您在线程 1 中读取值 5,请将其增加到 6,因为它不是原子线程 2 在此期间可能已读取值 5 并且已经将其增加到 6 或更多。所以线程 1 将值重置回以前的值,解释你看到的行为。
  • 同意。您的 32 位操作系统真的出了点问题。或者机器只有一个核心。
  • @James:你真的错过了这里的重点。 operator++() 不是 原子的。如果是这样,那么实现 Interlocked.Increment() 将是愚蠢的
  • @James:您不一定会在这里看到内存模型问题。您更有可能在 2 个 O/S 中看到线程调度的差异。正如其他人所说 - ++ 运算符不是原子的(请参阅 Julien 在这些 cmets 中的解释)。如果您只是更改您正在使用的 3 个线程中的任何一个被抢占/唤醒的时间,您可以考虑这些差距——您不一定会在这里看到位剪切。只是为了好玩,我在我的四核机器(win7/64 位)上运行它,我敢打赌我得到了数百个 BING! (在 x86 和 AnyCpu 配置中)
猜你喜欢
  • 2011-05-01
  • 1970-01-01
  • 2017-04-30
  • 2014-12-17
  • 2014-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-30
相关资源
最近更新 更多