Laden...

Threads und Controls

Letzter Beitrag vor 19 Jahren 13 Posts 3.122 Views
Threads und Controls

Folgendes Szenario:

es gibt eine Klasse A. diese Klasse bekommt beim Erstellen als Parameter Verweis auf ein Interface I. Nun startet Klasse A ein Thread T, um sagen wir Berechnung durchzuführen. Wenn Berechnung fertig ist, ruft Klasse A aus dem Thread T eine Methode des Interfaces I.

Nun Problem ist, dass in dieser Methode werden unter anderem auch Controls aktualisiert. Und das geht nicht, da Controls „singlethreadig“ (tolles Wort habe ich ausgedacht 🙂) sind.

Also, wie löse ich das Problem am besten?

Irgendwelche Ideen?

Hallo Xqgene,

mit Control.Invoke bzw. Control.BeginInvoke. Oder willst du auf was anderes hinaus?

herbivore

Wenn Du Zugriff auf ein "Control" hast, dann mit Control.Invoke (.....)

Wenn Du keinen Zugriff auf ein Control hast, dann brauchst Du Zugriff auf ein im UI-Thread erstelltes ISynchronizeInvoke - Objekt um dort Invoke zu machen...

PS: UI-Controls implementieren ISynchronizeInvoke daher haben diese die Invoke-Mehthode

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

genau das ist es, dass ich keinen Zugriff auf Controls habe. und ich möchte nur sehr ungern das Interface so erweitern, dass so ein Zugriff ermöglicht wird.

mal sehen... wenn nix anderes übrig bleibt.

Pack ein Objekt vom Type ISynchronizeInvoke auf Dein Interface... dann kannnste ein Form übergeben (hast aber nur Zugriff auf Invoke und dessen Kollegen).... das Interface bleibt so vollkommen offen.

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

hast recht. ich werde mal so machen.

danke für eure mühe.

Geht es nicht noch einfacher?

Klasse A ruft aus einem "Nicht-GUI"-Thread Methoden des Interfaces auf. Das Interface soll frei von Forms und anderem Schnickschnack bleiben. Die konkrete Implementierung des Interfaces aber, die dann aufgerufen wird, aktualisiert Controls wie du gesagt hast. D.h. sie hält Referenzen auf die Controls. Dann kann doch sie Control.Invoke zum Aktualisieren benutzen. Wozu das öffentliche Interface erweitern?

Gruss
Pulpapex

über diese Möglichkeit habe ich auch schon nachgedacht, aber allein der Gedanke, dass die Klasse, die sich hinter einem Interface versteckt, verantwortlich über mögliche "thread"-Spielchen der Hostklasse sein soll, gefällt mir noch weniger als ISynchronizeInvoke-Methode.

Uebrigens so kann man eine Methode in einer Klasse welche ISynchronizeInvoke (z.B: Control / Form usw.) bauen, dass es egal ist woher der Aufruf erfolgt.

Multithreads, DataGrid

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

Man kann es auch von der anderen Seite betrachten.

Control.Invoke ist ein Workaround für eine aus der Win-Api geerbte Unzulänglichkeit von Windows-Forms, nicht mit mehreren Threads umgehen zu können. Warum diese technische Eigenart veröffentlichen und dadurch das Interface versauen?

Es wird quasi unbenutzbar. Und es wird fehleranfälliger, da Members öffentlich gemacht werden, die nicht verwendet werden dürfen - jedenfalls nicht direkt.

Hier ein Beispiel wie ich das meine:

public interface IFileProcessorView {
   public string CurrentAction( get; set; );   // Nur über UIThread aufrufen.
   public string CurrentFile( get; set; );     // Nur über UIThread aufrufen.
   public double OverallProgress( get; set; ); // Nur über UIThread aufrufen.
   public double FileProgress( get; set; );    // Nur über UIThread aufrufen.
   public ISynchronizeInvoke UIThread { get; }
}

// Im Thread ausgeführte Methode.
private void ThreadMethod() {

   IFileProcessorView view = this.fileProcessorView;

   // Verboten!
   view.CurrentAction = "Deleting File ...";


   // Stattdessen folgender Code ...

   // Parameter für BeginInvoke vorbereiten.
   SetCurrentActionDelegate setCurrentAction = 
      new SetCurrentActionDelegate(SetCurrentAction);
   string[] action = new string[] {"Deleting File ..."};

   // BeginInvoke: im UIThread ausführen.
   view.UIThread.BeginInvoke(setCurrentAction, action);
}

// Für den Delegaten notwendige Hilfsmethode.
private void SetCurrentAction(string action) {
   IFileProcessorView view = this.fileProcessorView;
   view.CurrentAction = action;
}

// Delegate für Invoke.
private delegate void SetCurrentActionDelegate(string action);

Das wiederholt sich an allen Stellen wo auf das Interface zugegriffen wird.

Versteckt man den Code in der Implementierung, braucht man sich nicht mehr darum zu kümmern und kann die Interface-Members wieder direkt verwenden. Nur die Implementierung soll ja wissen, dass im Hintergrund Windows-Controls aktualisiert werden.

@Pulpalex

Noch schöner ist es wenn die Methode auf sich selber wrappt... siehe meinen Link...

Die Kapselung ist dadurch noch viel besser.... das Objekt wird somit ThreadSicher... bedingt allerdings dass das Objekt ISynchronizeInvoke implementiert.

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

das bsp von Pulpalex hat mich jetzt doch überzeugt.
und das wrappen von Programmierhans finde ich auch sehr interessant.

ich bedanke mich bei allen.

P.S. sollte jemand 'ne möglichkeit ohne Control.Invoke kennen/ausdenken, werde ich im tausend danke-schön-mails senden. 😁

Original von Xqgene

P.S. sollte jemand 'ne möglichkeit ohne Control.Invoke kennen/ausdenken, werde ich im tausend danke-schön-mails senden. 😄

Control.BeginInvoke
Control.EndInvoke

😁 😁 😁 😁 😁 😁 😁

aber bitte nicht vollspamen

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...