2010-06-07 1 views
5

Как я понимаю, модель памяти .NET на 32-разрядной машине гарантирует, что 32-разрядное слово записывает и читает как атомные операции, но не предоставить эту гарантию на 64-битные слова. Я написал быстрый инструмент для демонстрации этого эффекта на 32-разрядной ОС Windows XP, и я получаю результаты в соответствии с описанием этой модели памяти..NET 3.5SP1 64-разрядная модель памяти по сравнению с 32-разрядной моделью памяти

Тем не менее, я выполнил этот же исполняемый файл и запустил его на 64-разрядной ОС Windows 7 Enterprise и получаю совершенно разные результаты. Обе машины идентичны спецификациям только с установленными различными ОС. Я бы ожидал, что модель памяти .NET будет гарантировать запись и чтение BOTH 32-битных и 64-битных слов, которые будут атомарными в 64-битной ОС. Я нахожу результаты, полностью противоречащие BOTH-предположениям. 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 (интермедиат) никогда не пишутся и показывает, что 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 (длинный) и Бинг (интермедиат) оба отображаются! Почему сбой 32-разрядных операций, не говоря уже о 64-битных?

+0

Возможно ли, что вы могли отредактировать свой вопрос, используя форматирование «Код образца» на панели инструментов? Это невероятно сложно читать .. – JBRWilkinson

+0

Я правильно отформатировал его, но с символами TAB сначала вместо пробелов, и просмотр выглядел великолепно. Я был потрясен, когда обнаружил, что он полностью не смог опубликовать сообщение. Я исправил это сейчас. –

+0

У меня уже было исправлено: D – Femaref

ответ

4

В вашей нити обратного вызова вы делаете гораздо больше, чем просто писать или читать:

++longValue; 
++intValue; 

Ведение как чтение и запись не может быть гарантированно атомарными. Используйте Interlocked.Increment для обеспечения атомарности этой операции.

+0

Я не хочу, чтобы эти строки были явно атомарными. Я хочу, чтобы операция записи была гарантирована атомной моделью памяти. Это то, что я пытаюсь продемонстрировать. Очевидно, что я должен использовать Interlocked.Increment (ref intValue или longValue), но это приведет к поражению цели демонстрации. –

+3

Операция записи атомарна, это чтение + запись, которой нет, как сказал Дарин. Поэтому, если вы прочитаете значение 5 в первом потоке, увеличьте его до 6, так как это не атомный поток 2 может иметь значение 5 для чтения и уже увеличил его до 6 или более. Таким образом, поток 1 возвращает значение обратно к предыдущему значению, объясняя поведение, которое вы видите. –

+0

@Julien Да, но проверяемый случай в основном потоке Console.WriteLine заключается в том, что последнее записанное значение никогда не должно превышать последнее значение. Можете ли вы объяснить большие различия в значениях? Это последнее значение в выходных линиях. Это не один или два, это порядка 1000 или 100 000, что говорит о серьезном сдвиге бит. –