Laden...

Callback in MainThread ausführen

Erstellt von Johannes_meyer vor 13 Jahren Letzter Beitrag vor 13 Jahren 8.499 Views
J
Johannes_meyer Themenstarter:in
15 Beiträge seit 2010
vor 13 Jahren
Callback in MainThread ausführen

Hallo,

nach zig Beispiel-Codes und stundenlanger Suche in Büchern und Internet komm ich mit dieser eigentlich recht banalen Sache nicht weiter.

Ich hab einen Main-Thread. Der ruft per Delegate und BeginInvoke einen 2. Thread auf. Nach Beendigung des zweiten Threads wird die CallBack-Methode ausgeführt.
Die BeginInvoke-Anweisung und die CallBack-Methode stehen in der selben Klasse.

Die CallBack-Methode wird im 2. Thread ausgeführt. Ich möcht aber nach Beendigen des Threads eine Methode im MainThread ausführen.

Wenn die aufrufenden Klasse ein Windows.Forms-Control wäre, könnte ich das einfach durch

this.Invoke(new MethodenDelegate(Methode))

in der CallBack-Methode erreichen.

Die aufrufende Klasse ist aber kein Control.

Wie kann ich aus der CallBack-Methode eine Methode im MainThread aufrufen?

Gruß
Johannes

S
489 Beiträge seit 2007
vor 13 Jahren

Schau Dir mal die Klasse SynchronizationContext an. Die hat eine Send-Methode mit der kannst Du zurück.

61 Beiträge seit 2009
vor 13 Jahren

Hier eine Idee wie es auf jeden Fall funktioniert - vergleiche mal:

EDIT:
Mist, der Callback ist nicht im MainThread ^^


class MainThreadClass
    {
        public MainThreadClass() { }

        public void DoWork()
        {
            AnotherThread other = new AnotherThread();
            this.methodDelegator = new System.Threading.ThreadStart(other.DoWork);
            this.methodDelegator.BeginInvoke(new AsyncCallback(this.ThreadCallback), null);

            // so kann man das auch machen - ALTERNATIVE
            //System.Threading.ThreadStart d = new System.Threading.ThreadStart(other.DoWork);
            //d.BeginInvoke(new AsyncCallback(this.ThreadCallback), d);


            // ... (belibiger Code) 1. Thread
        }

        private System.Threading.ThreadStart methodDelegator;

        private void ThreadCallback(IAsyncResult result)
        {
            // Callback wird hier ausgeführt, wenn der Thread beendet wurde
            this.methodDelegator.EndInvoke(result);
            
            // so kann man das auch machen - ALTERNATIVE
            // wenn man oben die Alternative genommen hat
            //System.Threading.ThreadStart d = (System.Threading.ThreadStart)result.AsyncState;
            // d.EndInvoke(result);

            // wichtig ist das EndInvoke um den Vorgang abzuschließen
            // auf diese Weise können auch Rückgabewerte angenommen werden
        }
    }

    class AnotherThread
    {
        public AnotherThread() { }

        public void DoWork()
        {
            //... Code vom 2. Thread
        }
    }

In der Zeit vor fünf Minuten ist Jetzt die Zukunft. Jetzt ist die Gegenwart. Die Zeit, in der ich zu erzählen begonnen habe, ist die Vergangenheit von Jetzt und die Zukunft von der Gegenwart der Zeit, fünf Minuten bevor ich zu erzählen begann.

3.971 Beiträge seit 2006
vor 13 Jahren

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

J
Johannes_meyer Themenstarter:in
15 Beiträge seit 2010
vor 13 Jahren

Danke für die Antworten.

So wie Ruben es gepostet hat, hab ich es zuerst probiert. Allerdings wird dabei die CallBack-Methode nicht im MainThread sondern hier in AnotherThread ausgeführt. Das ist genau das Problem.

Ich hab jetzt eine noch einfachere Methode gefunden, die für meinen Zweck gut funktioniert:

Der BackgroundWorker

Hab gelesen, dass der genau deshalb eingeführt wurde. Die Ereignisse ProgressChanged und RunWorkerCompleted werden nämlich im aufrufenden Thread ausgelöst. Dadurch werden die entsprechenden Event-Handler auch im aufrufenden Thread abgearbeitet und nicht wie die CallBack-Methode in dem Code-Beispiel von Ruben im aufgerufenen Thread.

http://msmvps.com/blogs/manoj/archive/2005/11/03/74120.aspx

Naja, muss man auch erstmal wissen...

S
489 Beiträge seit 2007
vor 13 Jahren

Der BackgroundWorker von .NET arbeitet mit SynchronizationContext.

J
Johannes_meyer Themenstarter:in
15 Beiträge seit 2010
vor 13 Jahren

Aja, dann schließt sich der Kreis hier wieder 😉

6.911 Beiträge seit 2009
vor 13 Jahren

Hallo,

bezüglich BackgroundWorker ist die Diskussion in Eleganteste Methode: Aus Thread auf Controls einer Form zugreifen ganz interessant. (Ich hab vorher auch anders gedacht 😉

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo Johannes_meyer,

man kann die Ausführung einer Methode nur dann an einen anderen, schon laufenden Thread delegieren, wenn dieser Thread so programmiert ist, dass er entsprechende Anforderungen entgegennehmen kann.

Wenn dein Main-Thread stattdessen einfach irgendwelchen inhaltlichen Code abarbeitet, gibt es Chance, die Ausführung an den Main-Thread zu delegieren. Ein Thread führt stur den Code aus, den die Main- bzw. ThreadStart-Methode vorgibt. Es gibt keine Chance von außen den Thread zur Ausführung von anderem Code als diesem zu zwingen. Entsprechend kann SynchronizationContext (und damit auch BackgroundWorker) das natürlich auch nicht.

Das geht eben nur, wenn der Empfänger-Thread mitspielt. Einen Thread so zu programmieren, dass er Anforderungen entgegennehmen kann, kann man mit der schon genannten SyncQueue <T> - Eine praktische Job-Queue.

herbivore

61 Beiträge seit 2009
vor 13 Jahren

Stimmt, der MainThread muss die Anforderung irgendwie entgegennehmen.
Denn z.B. in einer normalen Konsolenanwendung gibt es keinen Punkt, an dem der MainThread irgendwelche Anforderungen von anderen Threads annehmen kann, außer es ruft sie ab.

Also gibt es nur zwei Möglichkeiten:

  • lösen über Synchronisation, oder
  • verzichten, wenn es nicht notwendig ist

Letztlich muss man sich nur fragen warum(?) man das brauch.
Theoretisch kann man dann auch 3 Threads machen:

  • einer der die Anforderungen abarbeitet (nehmen wir mal den MAIN)
  • einer der den eigentlichen Code ausführt (zwar der Hauptthread, aber nur woanders ausgeführt)
  • und einer der den 2. Thread darstellt

beide Threads geben ihre Anforderung an den MAIN-Thread und so wird alles im selben Thread ausgeführt - oder so ähnlich ^^

In der Zeit vor fünf Minuten ist Jetzt die Zukunft. Jetzt ist die Gegenwart. Die Zeit, in der ich zu erzählen begonnen habe, ist die Vergangenheit von Jetzt und die Zukunft von der Gegenwart der Zeit, fünf Minuten bevor ich zu erzählen begann.

3.971 Beiträge seit 2006
vor 13 Jahren

Hallo Ruben,
ist nicht böse gemeint, aber bitte befass dich erstmal mit den Grundlagen über multithreaded Programmierung.

Concurrency:What Every Dev Must Know About Multithreaded Apps(MSDN)

Theoretisch kann man dann auch 3 Threads machen:

  • einer der die Anforderungen abarbeitet (nehmen wir mal den MAIN)
  • einer der den eigentlichen Code ausführt (zwar der Hauptthread, aber nur woanders ausgeführt)
  • und einer der den 2. Thread darstellt

Du musst als erstes mal wissen, was du parallel ausführen lassen willst - besonders auch was sinnvoll ist und Mehrwert mit sich bringt - und du musst wissen, welche kritischen Bereiche deine Anwendung hat (welche Bereiche strikt zeitlich gesehen nacheinander laufen müssen) oder aber welche Anweisungen in einem bestimmten Thread laufen müssen (Stichwort STA COM-Objekte und WinForms)

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

61 Beiträge seit 2009
vor 13 Jahren

Werde ich, machen kleines_eichhoernchen. Mit Multithreading bin ich ja nicht so bewandert wie man sieht, aber ein interessantes Thema, finde ich.

Aber um auf etwas anderes zurück zu kommen...
Mit SynchronizationContext und AsyncOperation sind die Abläufe immer noch in verschiedenen Threads. Habe da etwas rumprobiert.
Bei einer Konsolenanwendung kann das durchaus egal sein. (z.B. wenn ein anderer Thread auf die Konsole schreiben will).

Aber jetzt ein Beispiel:
Ich habe eine Klasse in der über eine Methode ein Thread gestartet wird und über eine andere (sauber) beendet. Der gestartet Thread macht irgendwas (Berechnungen, Prüfungen, etc.) und wirft immer mal wieder ein Event. Das Ergebnis soll dann ausgegeben werden.
Bei einer Konsolenanwendung brauch ich nur die Klasse instanzieren und die Ergebnisse in den Event-Methoden auf der Konsole ausgeben.
In einer Formanwendung müsste ich aber jedes Event prüfen und dann über BeginInvoke oder Invoke im GUI-Thread ausführen.

Meine Frage daher:
Wie kann man eine Klasse so programmieren, dass man nie prüfen muss (wenn man die Klasse benutzt), ob das Event im richtigen Thread ausgeführt wird?
Beim BackgroundWorker kann man ein Event (ProgressChanged) auch ohne Prüfung abfangen.
Wie kann man das in einer Klasse implementieren, sodass es nicht relevant ist, ob man die Klasse in einer Konsole oder einer GUI-Anwendung nutzt?

Am besten wäre ein Codeausschnitt wie man einen Eventaufruf, in der Klasse durchführen kann, dass die angemeldeten Handler im richtigen Thread ausgeführt werden (zumindest bei GUI-Anwendungen).

In der Zeit vor fünf Minuten ist Jetzt die Zukunft. Jetzt ist die Gegenwart. Die Zeit, in der ich zu erzählen begonnen habe, ist die Vergangenheit von Jetzt und die Zukunft von der Gegenwart der Zeit, fünf Minuten bevor ich zu erzählen begann.

6.911 Beiträge seit 2009
vor 13 Jahren

Hallo Ruben,

lies dir hierzu bitte die Diskussion in Eleganteste Methode: Aus Thread auf Controls einer Form zugreifen durch. Das sollte dein Fragen klären.

Wurde auch schon weiter oben verlinkt.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"