2010-06-07 4 views
5

Si je comprends bien, le modèle de mémoire .NET sur une machine 32 bits garantit des écritures et des lectures de 32 bits comme étant des opérations atomiques, mais pas fournir cette garantie sur des mots de 64 bits. J'ai écrit un outil rapide pour démontrer cet effet sur un système d'exploitation Windows XP 32 bits et obtenir des résultats cohérents avec cette description du modèle de mémoire.Modèle de mémoire 64 bits .NET 3.5SP1 vs. modèle de mémoire 32 bits

Cependant, j'ai pris l'exécutable de ce même outil et l'ai exécuté sur un système d'exploitation Windows 7 Enterprise 64-bit et obtient des résultats très différents. Les deux machines sont des spécifications identiques seulement avec différents systèmes d'exploitation installés. Je m'attendais à ce que le modèle de mémoire .NET garantirait que les écritures et les lectures de TOUS les mots 32 bits et 64 bits soient atomiques sur un système d'exploitation 64 bits. Je trouve des résultats complètement contraires aux deux hypothèses. Il n'est pas démontré que les lectures et les écritures 32 bits soient atomiques sur ce système d'exploitation. Est-ce que quelqu'un peut m'expliquer pourquoi cela échoue sur un système d'exploitation 64-bit?

Code Tool:

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

Résultats sur Windows XP 32 bits:

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 

Remarquez comment BING (int) est jamais écrit et démontre que 32 bits lecture/écriture sont atomiques sur ce système d'exploitation 32 bits.

Résultats sur Windows 7 Enterprise 64 bits:

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 

Remarquez que BING (long) et BING (int) sont tous deux affichés! Pourquoi les opérations 32 bits échouent, et encore moins les 64 bits?

+0

Est-il possible que vous puissiez éditer votre question pour utiliser le format 'Exemple de code' dans la barre d'outils? C'est incroyablement difficile à lire .. – JBRWilkinson

+0

Je l'avais formaté correctement mais avec des caractères TAB initialement au lieu d'espaces et l'aperçu avait l'air bien. J'ai été choqué quand j'ai découvert qu'il avait complètement échoué en postant. Je l'ai réparé maintenant. –

+0

Je l'avais déjà corrigé: D – Femaref

Répondre

4

Dans votre rappel de fil vous font beaucoup plus que simplement écrire ou lire:

++longValue; 
++intValue; 

Faire la lecture et l'écriture ne peut pas être garantie atomique. Utilisez Interlocked.Increment pour assurer l'atomicité de cette opération.

+0

Je ne veux pas que ces lignes soient explicitement atomiques. Je veux que l'opération d'écriture soit garantie atomique par le modèle de mémoire. C'est ce que j'essaie de démontrer. Évidemment, je devrais utiliser Interlocked.Increment (ref intValue ou longValue) mais cela irait à l'encontre du but de la démonstration. –

+3

L'opération d'écriture est atomique, c'est la lecture + écriture qui ne l'est pas, comme l'a dit Darin. Donc, si vous lisez la valeur 5 dans le thread un, l'incrémenter à 6, puisque ce n'est pas le thread atomique 2 peut avoir la valeur de lecture 5 entre-temps et déjà incrémenté à 6 ou plus. Ainsi, le thread 1 réinitialise la valeur à une valeur précédente, expliquant le comportement que vous voyez. –

+0

@Julien Oui, mais le cas testé dans le thread principal Console.WriteLine est que la dernière valeur enregistrée ne doit jamais être supérieure à la valeur la plus récente. Pouvez-vous expliquer les grandes différences dans les valeurs observées? C'est la dernière valeur dans les lignes de sortie. Il n'y en a pas un ou deux, c'est de l'ordre de 1 000 ou de 100 000, ce qui indique qu'il y a de graves problèmes de cisaillement. –

Questions connexes