Laden...

Threadübergreifender Zugriff auf GUI-Elemente ohne Control.Invoke

Erstellt von DiViP vor 14 Jahren Letzter Beitrag vor 14 Jahren 3.244 Views
D
DiViP Themenstarter:in
27 Beiträge seit 2009
vor 14 Jahren
Threadübergreifender Zugriff auf GUI-Elemente ohne Control.Invoke

Hallo,

Wenn man aus einem fremden Thread GUI-Elemente verändert tritt bekanntlich eine Exception auf, wegen einer threadübergreifenen Veränderung von GUI-Elementen. Dieses Problem könnte man mit Control.Invoke lösen, aber das gefällt mir nicht, da ich die Sources der Form so einfach wie möglich halten will.
Wie ist es möglich, dass der fremde Thread sich darum kümmert, dass ein bestimmtes Event welches GUI-Elemente ändert, innerhalb des GUI-Threades aufgerufen wird. Ich möchte aber nicht dass der fremde Thread Control.Invoke auffruft, da dieser Thread nichts von dem Control wissen soll. Backgroundworker geht auch nicht, da ich diesen fremden Thread nicht selbst starte.
Kann man in einem fremden Thread eine Methode mit Parametern an den GUI-Thread hängen, welche dann innerhalb des GUI-Threades aufgerufen wird?

Ich hoffe meine Erklärung war verständlich. 😃

mfg
DiViP

203 Beiträge seit 2006
vor 14 Jahren

wenn der thread, nix von dem control weiß, wie ändert er es dann?

U
1.688 Beiträge seit 2007
vor 14 Jahren

Hallo,

hast Du schon die Forumssuche bemüht? Diese Fragestellung wurde schon (zu) oft beantwortet.

Ganz kurz: Der Thread stellt Ereignisse zur Verfügung, bei denen sich die GUI anmeldet und die dann in den Behandlungsroutinen das Control.Invoke/BeginInvoke aufruft - der Thread braucht die GUI nicht zu kennen.

Links auf weiterführende Beiträge hängst Du hier bitte an. Danke.

D
DiViP Themenstarter:in
27 Beiträge seit 2009
vor 14 Jahren

@esskar:
Der Thread stellt einen EventHandler bereit, welcher ein Ereignis in der Formklasse auslöst. Dort werden dann die GUI Elemente bearbeitet. Da aber eben dieses Event aus einem anderen Thread ausgelöst wird, entsteht dann eine Exception ohne ein Invoke.

@ujr:
Ich habe die Suche benutzt und die Lösung war immer ein Control.Invoke, welches ich aber, wie ich schon schrieb, nicht nutzen möchte.

1.820 Beiträge seit 2005
vor 14 Jahren

Hallo!

Control.Invoke ist meiner Meinung nach der beste Weg, um threadübergreifend die GUI zu beeinflussen, eine einfachere Lösung fällt mir jetzt auch nicht ein. Welche Bedenken hast du bzgl. des Sourcecodes? Unübersichtlichkeit, oder noch andere Aspekte?

Nobody is perfect. I'm sad, i'm not nobody 🙁

U
1.688 Beiträge seit 2007
vor 14 Jahren

Ich habe die Suche benutzt und die Lösung war immer ein Control.Invoke,

Fein - Deine Frage ließ allerdings nicht darauf schließen. Z. B. weil eben durch die Verwendung einer Ereignisbehandlung der Thread die Controls eben nicht kennen muss.

welches ich aber, wie ich schon schrieb, nicht nutzen möchte.

Die Erklärung fehlt bzw. ist nicht schlüssig.

P.S.: Schau Dir mal SynchronizationContext an.

D
DiViP Themenstarter:in
27 Beiträge seit 2009
vor 14 Jahren

Die Erklärung fehlt bzw. ist nicht schlüssig.

Die Erklärung habe ich gegeben. Der Source der Form soll so einfach wie möglich sein. Der Hauptteil spielt sich in einer DLL ab, dessen Ergebnisse in einer Form dargestellt werden sollen.

P.S.: Schau Dir mal SynchronizationContext an.

Das ist es! Danke 😃

Lösung

SendOrPostCallback OnCallback = null;
SynchronizationContext SyncContext;
//...
SyncContext = SynchronizationContext.Current; // wird im Constructor meiner Klasse aufgerufen
//...
SyncContext.Post(OnCallback, State); // wird im fremden Thread aufgerufen

OnCallback kann dann als Event im GUI-Thread verwendet werden und erzeugt dann keine Exception.

Vielen Dank!
mfg
DiViP

U
1.688 Beiträge seit 2007
vor 14 Jahren

Die Erklärung habe ich gegeben. Der Source der Form soll so einfach wie möglich sein.

Das meinte ich mit "nicht schlüssig" - ich weiß nicht, wo Invoke den Source kompliziert/komplex macht. Ist das nur ein "Gefühl"?

Prinzipiell sieht es ja mit SynchronizationContext ähnlich aus. Der Vorteil dabei ist jedoch, dass auch andere Ausführungsmodelle (WinForms, WPF, eigenes) unterstützt werden.

D
DiViP Themenstarter:in
27 Beiträge seit 2009
vor 14 Jahren

Das meinte ich mit "nicht schlüssig" - ich weiß nicht, wo Invoke den Source kompliziert/komplex macht. Ist das nur ein "Gefühl"?

Invoke ist nicht komplex oder kompliziert, es sind aber ein paar Codezeilen mehr. 😉 Ich bin was das angeht Perfektionist und möchte die Formseite so extrem! einfach halten, damit jeder User, die oben angesprochene DLL, ohne viel Aufwand/Wissen nutzen kann. (auch wenn ich in diesem Fall der einzige Nutzer bleiben werde 😃)

So genug, bevor das hier Off-Topic wird. 😃
Danke nochmals.

mfg

998 Beiträge seit 2007
vor 14 Jahren

Da du die Bib scheinbar für andere Entwickler einfach halten willst, lass dir gesagt sein das hier wohl die Merhheit der Leute Invoke kennt und dementsprechend deinen Code schneller mit Invoke verstehen wird als mit SynchronisationContext 😃

Gruß David

D
DiViP Themenstarter:in
27 Beiträge seit 2009
vor 14 Jahren

... die Merhheit der Leute Invoke kennt und dementsprechend deinen Code schneller mit Invoke verstehen wird als mit SynchronisationContext 😃

SynchronisationContext bekommt der User doch gar nicht zu sehen, was genau das Ziel meiner Frage war. Der User bekommt eine Klasse mit einen Eventhandler, welcher die Daten liefert, die angezeigt werden sollen und dieses Event soll frei sein von zusätzlichen Code, welcher nur dazu dient Threadübergreifende Prozesse zu steuern.

151 Beiträge seit 2003
vor 14 Jahren

CheckForIllegalCrossThreadCalls = false; wär auch ne Option, kann aber zu Problemen führen und sollte eigentlich nicht benutzt werden.

49.485 Beiträge seit 2005
vor 14 Jahren

Hallo moson,

CheckForIllegalCrossThreadCalls = false; [...] kann aber zu Problemen führen und sollte eigentlich nicht benutzt werden.

kann und wird zu Problemen führen und ist deshalb gerade keine Option. CheckForIllegalCrossThreadCalls ist - wie auch in der :rtfm: Doku steht, nur für Debuggingzwecke gedacht. Im Release sollte bzw. darf es keine threadübergreifenden Zugriffe mehr geben.

herbivore

5.299 Beiträge seit 2008
vor 14 Jahren

@DiViP: in [ExtensionMethods] CrossThread-Calls habich die Invokereien komplett vom gui-code weggekapselt.

Zum Vergleich Invoking - Synchronisation-Context: Ich seh da keinen Unterschied an Kompliziertheit, obman nu schreibt
Context.Post(delegate,...)
oder
Control.Invoke(delegate,...)
Tatsächlich kannichmir nie merken, welches von beiden nun seinerseits den Nebenthread blockiert, da es auf die Ausführung im GuiThread wartet: Context.Post oder .Send ?

Bei Control weissichs: Invoke wartet, BeginInvoke nicht.

Der frühe Apfel fängt den Wurm.

D
DiViP Themenstarter:in
27 Beiträge seit 2009
vor 14 Jahren

Context.Post oder .Send ?

Danke für die Info. Diesen Aspekt hatte ich gar nicht beachtet, ist aber ein sehr schöner Vorteil vom Synchronisation-Context, das man hier die Wahl zwischen asynchron (Post) und synchron (Send) hat. Habe nun den Context in meinem Code auf Send umgestellt, damit der DLL-Thread nicht davon läuft, weil das könnte durchaus passieren, wenn die GUI ein bisschen was zu tun hat. 😃

mfg
DiViP

49.485 Beiträge seit 2005
vor 14 Jahren

Hallo DiViP,

ist aber ein sehr schöner Vorteil vom Synchronisation-Context, das man hier die Wahl zwischen asynchron (Post) und synchron (Send) hat.

nur damit kein falsche Eindruck entsteht: Diese Wahl hat man bei Control.BeginInvoke (asynchron) Control.Invoke (synchron) auch.

Hallo ErfinderDesRades,

Ich seh da keinen Unterschied an Kompliziertheit,

in der Kompliziertheit liegt der Unterschied auch nicht. Es geht viel wichtiger um die Entkoppelung. Bei der Verwendung von Control.Invoke/BeginInvoke muss man System.Windows.Forms an einer Stelle einbinden, die mit dem GUI nichts zu tun hat. Außerdem kann man die Bibliothek dann nur in Windows-Forms-Programmen benutzen, wogegen SynchronizationContext mit allen Anwendungsarten (Forms, WPF, Web, Console, ...) funktioniert.

herbivore

5.299 Beiträge seit 2008
vor 14 Jahren

Ja, aber dafür isses (innerhalb von WinForm) mit Control.Invoke, hier: Application.OpenForms[0].BeginInvoke (ausse CrossThreadCalls) nochn bischen einfacher zu benutzen, weil man muß keinen SynchronisationContext initialisieren, und bei der Initialisierung gewährleisten, dasses auch im GuiThread initialisiert ist.

(@DiviP: Intern leitet in WinForm der SynchronisationContext auch an Control.Invoke() weiter)

Der frühe Apfel fängt den Wurm.

203 Beiträge seit 2006
vor 14 Jahren

ja, er will das control aber nicht an seine DLL übergeben.

5.299 Beiträge seit 2008
vor 14 Jahren

Welches Control? Welche Dll?

Wie gesagt: [ExtensionMethods] CrossThread-Calls kapseln das ganze Brimborium weg, und man kann schreiben statt:


AsyncMethod(blabla);

CrossThreadX.RunAsync(AsyncMethod, blabla);

dann läufts im Nebenthread

oder statt:


GuiMethod(blabla);

CrossThreadX.RunGui(GuiMethod, blabla);

dann läufts im Guithread

Der frühe Apfel fängt den Wurm.

3.511 Beiträge seit 2005
vor 14 Jahren

So wie ich das verstehe möchte DiVip eine unabhängige Klasse haben, also unabhängig von WinForms, WebForms etc. Bei deiner Extension Method müsste er aber ein Verweis auf System.Windows.Forms haben. Das fällt damit weg.

"Jedes Ding hat drei Seiten, eine positive, eine negative und eine komische." (Karl Valentin)

5.299 Beiträge seit 2008
vor 14 Jahren

Ich les immer nur, dass sein Code im Form möglichst einfach sein soll.

Und, dasser mit SynchronisationContext zufrieden ist, also was solls.

edit: 👅

Der frühe Apfel fängt den Wurm.