2015-04-08 10 views
39

Dieses Puzzle wurde bei NDC 2010 präsentiert. Es gibt Links zu Video von dort, aber sie sind alle gebrochen. Ich verstehe das Verhalten dieses Programms nicht; Warum hängt es?Eric Lippert und Neal Gafter C# Puzzle

class Woot 
{ 
    private static float PI; 
    private static bool initialized = doInitialize(); 

    private static bool doInitialize() 
    { 
     if (!initialized) 
     { 
      var thread = new Thread(() => { PI = 3.14f; }); 
      thread.Start(); 
      thread.Join(); // here 
     } 
     return true; 
    } 

    public static void Main(string[] args) 
    { 
     Console.WriteLine(PI); 
    } 
} 

Was ist die Ausgabe dieses Programms? Ist es:

  • 3,14
  • Wirft Ausnahme
  • Keine der oben genannten
+0

@Sayse, meinst du Metadaten Typ Objekt wurde noch nicht erstellt, wenn der Thread versucht, es zu verwenden? Das ist meine Vermutung. – Yola

+3

@Yola - Sie könnten setzen wollen "* Und ich kann das Verhalten des Programms nicht verstehen. Es hängt. *" Am ** Ende ** Ihrer Frage, das Spiel nicht wegzugeben. :) –

+1

@poke Das ist eine ganz andere Frage. Selbst wenn der Autor der anderen Frage die gleiche Frage stellen würde - diese Frage ist klarer. – Sebastian

Antwort

29

Ich glaube, dass Problem durch statisches Feld initalizator verursacht wird. Ich habe festgestellt, dass neue Thread nur gestartet wird, wenn doInitialize fertig ist (trotz thread.Start() heißt - ) also nehme ich an, dass CLR andere Threads blockiert, um den gleichzeitigen Zugriff zu vermeiden/doppelte Feldinitialisierung.

Um zusammenzufassen: Das neu erstellte Thread von CLR wird nicht gestartet gleichzeitigen Zugriff zu vermeiden, aber Haupt initialisiert Thread wartet für Kinder Thread getan werden, was bedeutet, Deadlock.

bearbeiten

@Sebastian (in einem Kommentar) Link vorgeschlagen, dass meine Theorie beweisen kann: http://blogs.msdn.com/b/pfxteam/archive/2011/05/03/10159682.aspx

+1

Sehr gut, wahrscheinlich die richtige Antwort, brauchen einige Beweise Links. – Yola

+3

Ich denke, das sind die relevanten Links: [Blog1] (http://blogs.msdn.com/b/pfxteam/archive/2011/05/03/10159682.aspx) - [Blog2] (http: // Blog. duncanworthy.me/swdev/csdotnet/constructor-gotchas-part3-deadlock/) und schließlich [ECMAScript] (http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf#page = 178 & zoom = auto, 87,610 ") – Sebastian

+0

@Yola Ja, ich suche in MSDN nach einem Beweis. Das einzige, was ich bisher gefunden habe, ist eine Aussage, dass die statische Feldinitialisierung threadsicher ist, aber keine Aussage wie thread- Sicherheit ist gewährleistet –

16

doInitialize ausgeführt wird, wenn der statische Typ aufgebaut ist, und hält dann an, bis der Faden, der PI setzt endet.

Der Thread, der versucht, PI festzulegen, kann jedoch nicht ausgeführt werden, bis der Typ initialisiert wird. Dies geschieht erst, wenn die Initialisierung (statischer Konstruktor und statische Initialisierer) beendet ist - was wie oben noch nicht geschehen ist.

Also das Programm Deadlocks.

Siehe auch this answer von Eric Lippert.

+1

Dies ist höchst irreführend.Wenn statische Member nicht gesetzt werden können, bis der Typinitialisierer abgeschlossen ist, dann kann der Typinitialisierer keine statischen Membervariablen setzen - eindeutig falsch. Die Tatsache, dass 'PI' erst nach th gesetzt wird Der vollständige statische Konstruktor ist ein Effekt der besonderen Anordnung von Code in diesem speziellen Programm, keineswegs eine universelle Aussage. Es wäre besser zu sagen "Der Code, der' PI' setzt, kann nicht ausgeführt werden ... " –

4

Der Thread wird nie enden, also thread.Join() wird nie zurückkehren. doInitialize() wird vom statischen Konstruktor ausgeführt. Im statischen Konstruktor versuchen wir, die statische Eigenschaft zu setzen, aber wir können nicht auf die statische Eigenschaft zugreifen, wenn der statische Konstruktor nicht beendet ist. Rennen