2010-06-07 6 views
5

Wie ich es verstehe, garantiert das .NET-Speichermodell auf einer 32-Bit-Maschine 32-Bit-Schreib- und Leseoperationen als atomare Operationen, tut dies aber nicht bieten Sie diese Garantie für 64-Bit-Wörter. Ich habe ein schnelles Tool geschrieben, um diesen Effekt auf einem Windows XP 32-Bit-Betriebssystem zu demonstrieren, und erhalte Ergebnisse, die mit dieser Speichermodellbeschreibung übereinstimmen..NET 3.5SP1 64-Bit-Speichermodell vs. 32-Bit-Speichermodell

Allerdings habe ich die ausführbare Datei dieses gleichen Tools und führen Sie es auf einem Windows 7 Enterprise 64-Bit-Betriebssystem und bekomme sehr unterschiedliche Ergebnisse. Beide Maschinen sind identische Spezifikationen nur mit verschiedenen Betriebssystemen installiert. Ich hätte erwartet, dass das .NET-Speichermodell das Schreiben und Lesen von BEIDEN 32-Bit- und 64-Bit-Wörtern auf einem 64-Bit-Betriebssystem garantiert. Ich finde Ergebnisse, die völlig gegen beide Annahmen verstoßen. 32-Bit-Lese- und Schreibvorgänge werden in diesem Betriebssystem nicht als atomar beschrieben.

Kann mir jemand erklären, warum dies auf einem 64-Bit-Betriebssystem fehlschlägt?

Werkzeugcode:

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; 
     } 
    } 
} 

Ergebnisse auf Windows XP 32-Bit:

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 

Beachten Sie, wie BING (int) wird nie geschrieben und zeigt, dass 32-Bit-Reads/Writes sind atomar auf dieses 32-Bit-Betriebssystem.

Ergebnisse auf Windows 7 Enterprise 64-Bit:

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 

Beachten Sie, dass BING (lang) und BING (int) beide angezeigt! Warum scheitern die 32-Bit-Operationen, geschweige denn die 64-Bit-Operationen?

+0

Ist es möglich, dass Sie Ihre Frage bearbeiten können, um die "Codebeispiel" -Formatierung in der Symbolleiste zu verwenden? Es ist unglaublich schwer zu lesen. – JBRWilkinson

+0

Ich hatte es richtig formatiert, aber mit TAB-Zeichen zunächst statt Leerzeichen und die Vorschau sah gut aus. Ich war schockiert, als ich feststellte, dass es beim Entsenden völlig fehlgeschlagen war. Ich habe es jetzt behoben. –

+0

Ich hatte es schon behoben: D – Femaref

Antwort

4

In Thread Rückruf tun Sie viel mehr als nur das Schreiben oder Lesen:

++longValue; 
++intValue; 

tun Lesen und Schreiben können nicht garantiert werden atomar sein. Verwenden Sie Interlocked.Increment, um die Atomarität dieser Operation sicherzustellen.

+0

Ich möchte nicht, dass diese Zeilen explizit atomar sind. Ich möchte, dass die Schreiboperation vom Speichermodell atomar garantiert wird. Das versuche ich zu demonstrieren. Offensichtlich sollte ich Interlocked.Increment verwenden (ref intValue oder longValue), aber das würde den Zweck der Demonstration zunichte machen. –

+3

Die Schreiboperation ist atomar, es ist das Lesen + Schreiben, was nicht ist, wie Darin sagte. Wenn Sie also den Wert 5 im ersten Thread lesen, erhöhen Sie ihn auf 6, da der atomare Thread 2 in der Zwischenzeit den Wert 5 gelesen und ihn bereits auf 6 oder mehr erhöht hat. Daher setzt Thread 1 den Wert auf einen vorherigen Wert zurück und erklärt das Verhalten, das Sie sehen. –

+0

@Julien Ja, aber der Fall, in dem im Console.WriteLine-Thread getestet wird, ist, dass der zuletzt aufgezeichnete Wert nie größer als der letzte Wert sein sollte. Kannst du die großen Unterschiede in den Werten erklären? Das ist der letzte Wert in den Ausgabezeilen. Es geht nicht um eins oder zwei, es liegt in der Größenordnung von 1.000 oder 100.000, was darauf hinweist, dass etwas ernsthaftes Scheren vor sich geht. –