2009-06-03 5 views
2

.Nets seltsame Sperrensemantik nervt mich wieder.C#: Warte bis die Variable nicht null wird

Ich starte einen Thread, der Kind-Thread startet abwechselnd ein Formular. Der übergeordnete Thread sollte warten, bis das Formular erstellt wird.

Mein erster Versuch war ein Monitor zu verwenden, um die Formularvariablen zu sehen:

private void OpenForm() 
{ 
    if (FormThread == null) 
    { 
     Monitor.Enter(Form); 
     FormThread = new Thread(FormStub); 
     FormThread.SetApartmentState(ApartmentState.STA); 
     FormThread.Start(); 
     Monitor.Wait(Form); 
     Monitor.Exit(Form); 
    } 
} 

private void FormStub() 
{ 
    Form = new ConnectorForm(); 
    Monitor.Enter(Form); 
    Monitor.PulseAll(Form); 
    Monitor.Exit(Form); 
    Application.Run(Form); 
} 

... Dies löst eine Ausnahme. Monitor.Enter() schlägt fehl, seit Form == null.

Ich könnte sehr einfach eine Dummy-Integer oder etwas erstellen (ich glaube, ich werde die FormThread-Variable canabalize), aber ich fragte mich, ob es eine elegantere Lösung gab.

Antwort

4

Bessere Synchronisation primitive für diesen Fall:

private ManualResetEvent mre = new ManualResetEvent(false); 

private void OpenForm() 
{ 
    if (FormThread == null) 
    { 
     FormThread = new Thread(FormStub); 
     FormThread.SetApartmentState(ApartmentState.STA); 
     FormThread.Start(); 
     mre.WaitOne(); 
    } 
} 

private void FormStub() 
{ 
    Form = new ConnectorForm(); 
    mre.Set(); 
    Application.Run(Form); 
} 
+0

Dies scheint um genau das Primitive zu sein, nach dem ich gesucht habe. Ich habe es vermisst, als ich die Liste der Sachen in System.Threading gelesen habe, anscheinend ... meine Augäpfel suchten nach "Event" oder so. – Thanatos

0

Führt keine Spin-Warte auf den aktuellen Thread löschen den gesamten Punkt der Verwendung eines separaten Threads, um das neue Formular zu lanchieren? Wenn ich etwas nicht falsch verstehe, möchte ich das neue Formular nur synchron erstellen. (Gibt es einen Grund braucht es in einem anderen STA wohnen?)

+0

Es sieht aus wie er möchte, dass seine Hauptschleife von dem anderen laufen Thread aus irgendeinem Grund ...? – jerryjvl

+0

Das kleinere Problem ist, dass es mir erlaubt, sich nicht über das Threading-Modell des Hauptthreads Sorgen zu machen, und ob es Formulare unterstützt. Der größere Vorteil besteht darin, dass der Hauptthread die Steuerung zurückerhält und Vorgänge so ausführen kann, als wäre er ein einziger Thread, während sich dieses Formular in einem anderen Thread befindet und den Benutzer unterhalten und informiert hält. Meine Hoffnung ist, dass die End-API zum Hauptthread sehr nett ist. – Thanatos

0

Sie die folgenden versuchen könnten, die ein einziges verwendet object/Monitor als Nachrichtenmechanismus:

private void OpenForm() 
{ 
    if (FormThread == null) 
    { 
     object obj = new object(); 
     lock (obj) 
     { 
      FormThread = new Thread(delegate() { 
       lock (obj) 
       { 
        Form = new ControllerForm(); 
        Monitor.Pulse(obj); 
       } 
       Application.Run(Form); 
      }); 
      FormThread.SetApartmentState(ApartmentState.STA); 
      FormThread.Start(); 
      Monitor.Wait(obj); 
     } 
    } 
} 

Der ursprüngliche Thread hält die Sperre bis es ruft Monitor.Wait; Dadurch kann der zweite Thread (bereits gestartet) das Formular erstellen, den ursprünglichen Thread zurück ins Leben pulsieren und loslassen. Der ursprüngliche Thread wird dann erst beendet, nachdem Form existiert.

+0

Was passiert, wenn 'Monitor.Pulse' zwischen' FromThread.Start' und 'Monitor.Wait' aufgerufen wird? – Dialecticus

+0

@Dialecticus merken: die einzige Person, die 'Pulse' nennen kann, ist die Person, die das Schloss hat. Und bis Sie 'Wait' aufrufen, wird diese Sperre verwendet. Die Antwort darauf lautet entweder "es kann nicht sein" oder expliziter: "Der Thread, der' Monitor.Pulse' aufruft, erhält eine Ausnahme, weil * sie keine Sperre haben * " –

0

Ich neige dazu, die AutoResetEvent für Fälle wie diese verwenden:

private AutoResetEvent _waitHandle = new AutoResetEvent(false); 

private void OpenForm() 
{ 
    Thread formThread = new Thread(FormStub); 
    formThread.SetApartmentState(ApartmentState.STA); 
    formThread.Start(); 
    _waitHandle.WaitOne(); 

    // when you come here FormStub has signaled     
} 

private void FormStub() 
{ 
    // do the work 

    // signal that we are done 
    _waitHandle.Set(); 
} 
-1

ein statisches Bool Flag verwenden, ob das Formular geladen ist. Es ist atomar, also muss nicht gesperrt werden.

Im Haupt Code nur etwas tun, wie

while(!formRun) { Thread.Sleep(100); } 

Die eigentliche Frage ist, warum tun Sie das? Normalerweise möchten Sie, dass der Hauptthread GUI-Stuff ausführt, und sekundäre Threads, um den Helpercode auszuführen. Wenn Sie erklären, warum Sie es brauchen, können wir vielleicht eine bessere Technik entwickeln.

+0

Bessere Hoffnung, dass FormRun flüchtig ist, dann .. das ist nicht garantiert, jemals sonst zu beenden. –

0

andere Möglichkeit, ein Eventwaithandle zu übergeben ist es als Parameter an FormStub passieren (so dass es nicht Modell Krempel Ihr Objekt):

static void Main() 
{ 
    Application.EnableVisualStyles(); 
    Application.SetCompatibleTextRenderingDefault(false); 

    EventWaitHandle e = new EventWaitHandle(false, EventResetMode.ManualReset); 
    Thread t = new Thread(FormStub); 
    t.SetApartmentState(ApartmentState.STA); 
    t.Start(e); 
    e.WaitOne(); 
} 

static void FormStub(object param) 
{ 
    EventWaitHandle e = (EventWaitHandle) param; 
    Form f = new Form1(); 

    e.Set(); 
    Application.Run(new Form1()); 
}