2010-06-07 67 views
5

據我所知,32位機器上的.NET內存模型保證32位字的寫入和讀取是原子操作,但不是在64位字上提供此保證。我已經編寫了一個快速工具來在Windows XP 32位操作系統上演示這種效果,並獲得與該內存模型描述一致的結果。.NET 3.5SP1 64位內存模型與32位內存模型

但是,我採用了這個相同的工具的可執行文件,並在Windows 7 Enterprise 64位操作系統上運行它,結果卻截然不同。這兩臺機器都是相同的規格,只是安裝了不同的操作系統。我預計.NET內存模型可以保證在64位操作系統上寫入和讀取32位和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企業版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(長)和Bing(INT)都顯示出來!爲什麼32位操作失敗,更不用說64位操作了?

+0

是否有可能編輯您的問題以使用工具欄上的「代碼示例」格式?這是難以置信的難以閱讀.. – JBRWilkinson 2010-06-07 16:57:04

+0

我已經格式正確,但與TAB字符最初,而不是空間和預覽看起來不錯。當我發現發佈完全失敗時,我感到震驚。我現在修好了。 – 2010-06-07 16:58:59

+0

我已經修好了:D – Femaref 2010-06-07 16:59:38

回答

4

在你的線程回調你正在做的遠不止簡單地寫或讀:

++longValue; 
++intValue; 

做讀取和寫入不能保證是原子。使用Interlocked.Increment確保此操作的原子性。

+0

我不希望這些行顯式原子化。我希望寫操作能夠被內存模型保證爲原子。這就是我想要展示的。很明顯,我應該使用Interlocked.Increment(ref intValue或longValue),但這會挫敗演示的目的。 – 2010-06-07 17:03:36

+3

如Darin所說,寫入操作是原子操作,它的讀寫操作不是。因此,如果讀取線程1中的值5,則將其增加到6,因爲它不是原子線程2可能在此期間讀取值5,並已將其增加到6或更多。因此,線程1將該值重置爲以前的值,解釋您所看到的行爲。 – 2010-06-07 17:14:46

+0

@Julien是的,但在Console.WriteLine主線程中進行測試的情況是,最後記錄的值永遠不會大於最新的值。你能解釋一下所看到的價值觀的巨大差異嗎?這是輸出行中的最後一個值。它沒有一兩個關閉,大約有1,000或100,000個數量級,表明正在進行一些嚴重的剪切。 – 2010-06-07 17:22:53