2009-06-24 14 views
7

I eine Art von Plug-in-Modell haben, in dem verschiedene komplexe Benutzersteuerungen in DLLs und geladen und instanziiert zur Laufzeit gespeichert werdenErstellen von Steuerelementen in einem nicht-UI-Thread

Activator.CreateInstanceFrom(dllpath, classname). 

Verwendung Da ich bin Lade einige davon wollte ich im Hintergrund tun, indem ich meine Benutzeroberfläche ansprechbar halte, indem ich einen neuen Thread für das Laden erstelle. Die Kontrollen werden dann zum Hauptformular weitergeleitet und bei Bedarf angezeigt.

Dies scheint gut zu funktionieren - bis ich versuche, eine Eigenschaft auf einem verschachtelten Steuerelement auf einem dieser Benutzersteuerelemente, z. im Ereignishandler einer Schaltfläche, die eine Cross Threading-Ausnahme auslöst. Mir ist klar, dass ich dies vermeiden kann, indem ich InvokeRequired jedes Mal überprüfe, wenn ich auf eine Eigenschaft zugreife, aber ich muss mir darüber keine Gedanken machen, wenn ich den Code für die Benutzersteuerelemente schreibe (besonders da es andere gibt, die diese Codes schreiben) nicht immer erinnern).

Also meine Frage ist, gibt es eine sichere Möglichkeit zu tun, was ich versuche, oder wie sollte ich am besten über das Laden dieser Steuerelemente im Hintergrund gehen? Oder ist es im Grunde unmöglich und muss ich mich an den Hauptthread zum Erstellen von Steuerelementen halten?

Ich hoffe, dass die Informationen, die ich zur Verfügung gestellt habe, genug sind, um meine Situation klar zu machen; Wenn nicht, würde ich gerne Code-Beispiele ausarbeiten und zur Verfügung stellen.

+0

"bis ich versuche, eine Eigenschaft zu setzen" - ist das im Thread oder später? –

+0

Das ist später, nachdem die Benutzersteuerung zum Hauptformular (wie in form.controls.add (mycontrol) –

Antwort

3

Ist gut, um die DLLs zu laden und die Steuerobjekte im Hintergrund zu erstellen, aber das Steuerelement muss das Formular im Hauptthread und alle Benutzerinteraktion sowie jede programmatische Änderung der Steuerelementeigenschaften hinzugefügt werden (nachdem es war erstellt) muss im Hauptthread vorkommen. Es gibt einfach keinen Weg, wie Sie wahrscheinlich schon wissen. Jetzt, wenn das Laden dieser DLLs und die Erstellung von Steuerelementen nicht ewig dauert, sehe ich keinen Grund, dies in separaten Hintergrundthreads zu tun.

Wenn bestimmte von den Steuerelementen ausgeführte Aktionen die Benutzeroberfläche blockieren und im Hintergrund auftreten sollen, ist dies eine andere Geschichte. Sie sollten jede Aktion auf individueller Basis prüfen und explizite Methoden für die Kommunikation zwischen UI-Threads erstellen und Hintergrundthreads.

Versuch, einen generischen zu tun One-fits-all-Framework, das (in der Invoke blockiert werden, da alle Threads) funktioniert auf das gleiche in UI-Thread-Modus und im Hintergrundmodus und einfach überprüft die InvokeRequired Ergebnisse manchmal in schlechten Leistung oder selbst in (unerkannten) Deadlocks, sobald die Anwendung eine angemessene Komplexität erreicht. Das Ausführen aller Aktualisierungen async in BeginInvoke, die jede Methode einzeln in Betracht ziehen, kann zu Datenkonsistenzproblemen führen (dh Steuerelemente können sich aufgrund der Umkehrung der Aufrufreihenfolge zwischen Threads in den Status zurück rechtzeitig aktualisieren).

+0

Ja, ich habe das Gefühl, dass ich versucht habe, ein bisschen zu schlau zu sein. Ich habe meinen ersten Versuch mehr oder weniger aufgegeben und bewege mich ähnlich wie in Ihrem zweiten Absatz. Danke für die Tipps in Par. 3; Das hatte ich nicht bemerkt. –

+0

Persönlich bevorzuge ich die Verwendung einer Warteschlange (http://msdn.microsoft.com/en-us/library/7977ey2c.aspx), um zwischen den Threads zu kommunizieren: Der Hintergrundthread fügt der Warteschlange Einträge ('Ergebnisse') hinzu UI-Thread überprüft die Warteschlange im Leerlauf und aktualisiert die Steuerelemente (dies ist ein typischer Erzeuger/Verbraucher). Sie müssen jedoch sorgfältig abwägen, ob der Hintergrund die Fähigkeit der Benutzeroberfläche nicht übertreffen kann, Schritt zu halten, was besonders bei Gittern leicht passieren kann, sodass die Warteschlange außer Kontrolle gerät, wenn Sie keine besonderen Maßnahmen ergreifen. –

1

Das Codebeispiel in this answer bietet eine elegante Lösung für dieses Problem.

-Code aus dieser Antwort zitiert:

public void UpdateTestBox(string newText) 
{ 
    BeginInvoke((MethodInvoker) delegate { 
     tb_output.Text = newText; 
    });   
} 

... obwohl in Ihrem Fall würden Sie BeginInvoke auf den Kontrollen selbst nennen:

public void UpdateTestBox(string newText) 
{ 
    tb_output.BeginInvoke((MethodInvoker) delegate { 
     tb_output.Text = newText; 
    });   
} 
+0

) pranted wurde, sehe ich noch nicht ganz, wie dies helfen wird. Ich meine, ja, du hast Recht, natürlich hilft das Verwenden von Invoke/BeginInvoke, aber genau das versuche ich zu vermeiden. Oder vielleicht habe ich einfach nicht verstanden, worauf Sie hinauswollen. –

+1

Wenn Sie die Steuerelemente in einem anderen Thread als dem UI-Thread erstellen, kann ich nicht sehen, wie Sie von Invoke/BeginInvoke wegkommen. Der vorgeschlagene Code ist jedoch viel einfacher als die if (ctl.InvokeRequired) [...] Variante. Die sofortige Bestrafung von Multithread ist, dass Sie Ihre Synchronisation tun müssen;) –

1

Nicht zu wissen, die Details des Mechanismus InvokeRequired Ich habe ein wenig experimentiert und afaik kann die meisten Eigenschaften in einem Thread setzen, solange es nicht parented ist (dh hinzugefügt zu einer Control.Controls Eigenschaft).

Sie sollten also in der Lage sein, Ihre Steuerelemente vorzubereiten, sie in einer Liste speichern und sie an die Hauptoberfläche in einer aufgerufenen Methode anfügen.


Edit: Ich glaube nicht, dass es wichtig ist, welcher Thread ein Steuerelement erstellt. Es sollten also die normalen Regeln gelten, dh Sie können nur mit Steuerelementen aus dem Haupt-Windows-Thread arbeiten. Und ich denke, das Kriterium ist HandleCreated und nicht Parented. Solange das nicht passiert ist, bekommst du eine kleine Pause.

+0

Ja, es scheint die Elternschaft ist, was mich tötet. Ihre Idee ist nicht so schlecht, wird aber hier nicht helfen, da die Eigenschaften, die ich einstelle, alle auf eine Benutzerinteraktion zurückzuführen sind, mit anderen Worten * nachdem * das Steuerelement an das Hauptformular weitergegeben und angezeigt wurde. Das war vielleicht nicht ganz klar aus meiner Frage. –

+0

Es war klar, aber ich mache das ohne Probleme. –

Verwandte Themen