Laden...

Gui nicht blocken und auf Ende des Asynch-Aufrufes warten

Erstellt von chavez vor 15 Jahren Letzter Beitrag vor 15 Jahren 1.513 Views
C
chavez Themenstarter:in
252 Beiträge seit 2007
vor 15 Jahren
Gui nicht blocken und auf Ende des Asynch-Aufrufes warten

Also ich habe folgendes Szenario:
Ein Programm das mir WAV-Files in alle möglichen Formate konvertiert. Damit die GUI nicht blockiert rufe ich das Ganze asynchron auf. Jezt will ich bis zum Ende des asychronen Aufrufes warten ohne dass die GUI blockiert. Erst dann soll der nachfolgend Code ausgeführt werden.
Bisher löse ich das folgendermaßen:


        //Hier passiert vorher etwas

        EncodeAudioHandler eah = new EncodeAudioHandler(EncodeAudio);
        IAsyncResult result = eah.BeginInvoke("src", "dst", "codec -params", null, null);
        while (!result.IsCompleted)
        {
          Application.DoEvents();
          Thread.Sleep(100);
        }

        //Hier passiert auch noch etwas, allerdings erst nach dem Encoden

Die while Schleife mit den Application.DoEvents() gefällt mir ganz und gar nicht.
Gibt es da eine bessere/elegantere Lösung?

Muss man EndInvoke eigentlich immer aufrufen? Wenn ich keinen Rückgabewert erwarte, dann lasse ich das immer weg. Laut diesem MSDN-Artikel sollte man immer EndInvoke() aufrufen?

B
387 Beiträge seit 2005
vor 15 Jahren

Ich würde das ganze in einem BackgroundWorker laufen lassen, und am Ende den Gui-Thread per Control.BeginInvoke informieren, dass das Konvertieren abgeschlossen ist.

5.742 Beiträge seit 2007
vor 15 Jahren

Hallo chavez,

Jezt will ich bis zum Ende des asychronen Aufrufes warten ohne dass die GUI blockiert

Übergib der Begin... Methode einfach als vorletzten Parameter ein AsyncCallback mit der Methode, die nach dem Abschluss aufgerufen werden soll.
In dieser Methode musst du dann mit Control.BeginInvoke arbeiten.
Dein GUI sollte dann nicht blockiert werden.

Muss man EndInvoke eigentlich immer aufrufen? Wenn ich keinen Rückgabewert erwarte, dann lasse ich das immer weg.

Ja, auch dann sollte man es aufrufen.
In EndInvoke werden eventuell Ressourcen freigegeben.
Um das passende IAsyncResult wieder zur Verfügung zu haben, kann man das state object verwenden.

Ich würde das ganze in einem BackgroundWorker laufen lassen,

Wenn bereits eine BeginInvoke Methode zur Verfügung steht, ist das nicht notwendig.

C
chavez Themenstarter:in
252 Beiträge seit 2007
vor 15 Jahren

Das mit dem Callback funktioniert leider in meinem Fall nicht.
Ich arbeite alle Tracks einer Disc ab und schreibe Informationen darüber in ein XML-Dokument.
Hier der Aufbau der Methode:


//Alle Album-Infos werden ins XML geschrieben.

//Alle Tracks werden abgearbeitet
foreach (Track track in disc.Tracks)
{
   //Alle Track-Infos werden ins XML geschrieben(Interpret, Titel, Dauer,..)

   //Hier passiert der Aufruf fürs Encoding

   //Hier wird der Pfad zum File und der MD5-Hash ins XML geschrieben.
}

Im Callback habe ich ja dann keinen Zugriff mehr auf das XML-Dokument(ok, das könnte ich außerhalb der Methode deklarieren) und weiters werden dann ja alle Tracks sofort encodiert, da die foreach ja sofort für alle Tracks durchlaufen wird.
Mein Ziel ist, dass 1 Track nach dem anderen encodiert wird und die GUI dabei nicht blockiert. Dafür gibt es aber anscheinend keine andere Lösung bzw will mir keine einfallen als die while Schleife und Application.DoEvents()?

5.742 Beiträge seit 2007
vor 15 Jahren

Mein Ziel ist, dass 1 Track nach dem anderen encodiert wird

Das sollte sich auch realisieren lassen.

Du könntest jeweils das Verarbeiten des nächsten Tracks anstoßen, wenn der vorherige fertig encodiert ist.

Eine andere, klassische Möglichkeit zur Threadsynchronisation liegt darin, ein synchronisiertes Queue zu verwenden, in das der eine Thread die Arbeit legt und aus dem der andere sie wieder herausholt.

Deutlich besser wäre in meinem Augen aber, die gesamte Schleife in den Thread zu verlegen - oder tatsächlich, wie von Blacal vorgeschlagen, in einen BackgroundWorker, wenn du den Fortschritt berichten willst.

Allerdings verstehe ich nicht, was so schlimm daran ist, mehr als einen Titel zu encodieren (vielleicht 2 oder auch 3) - gerade Multicore CPUs nutzt man so meist nicht vollständig.

C
chavez Themenstarter:in
252 Beiträge seit 2007
vor 15 Jahren

Also der User klickt auf den Convert Button. Dann wird dieser disabled und die erwähnte Convert-Methode gestartet. Diese schreibt die Album und Track Information in ein XML und konvertiert die Tracks.
Das XML hat folgende Struktur:


<album>
<titel>testalbum<\titel>
<interpret>ich<\interpret>
...
<tracks>
<track>
<number>01<number>
<titel>track01<\titel>
...
<md5>acf412..<\md5>
<\track>
.
.
.
<\tracks>
<\album>

Klar ist das gut wenn mehrer Tracks parallel konvertiert werden.
Aber wenn ich das jetzt mittels Callback löse, dann habe ich ja im Callback keinerlei Info darüber welcher track gerade fertig konvertiert wurde und kann daher auch nicht das entsprechnde md5 node ins XML schreiben. Oder übersehe ich etwas?
Außerdem will ich den Convert button erst wieder enablen sobald der Konvertiervorgang abgeschlossen ist. In diesem Fall wird er ja sofort wieder enabled.


private void btEncode_Click(object sender, EventArgs e)
{
   this.btEncode.Enabled = false;
   EncodeDisc(src, dst, encodingparams);
   MessageBox.Show("Konvertierung fertig");
   this.btEncode.Enabled = true;       
}

Threadsynchronisierung wäre sicher eine Möglichkeit, aber im Prinzip kann ich mir den Aufwand ja mit meiner quick&dirty while Schleife sparen.

5.299 Beiträge seit 2008
vor 15 Jahren

Der frühe Apfel fängt den Wurm.

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo chavez,

mach es doch nicht komplizierter als es ist. Ich würde das Ganze in einem BackgroundWorker laufen lassen und am Ende im RunWorkerCompleted-Handler tun, was immer du am Ende der Konvertierung tun willst. Im GUI zu warten ist einfach der falsche Ansatz und nie nötig.

Siehe auch [FAQ] Warum blockiert mein GUI?

herbivore

C
chavez Themenstarter:in
252 Beiträge seit 2007
vor 15 Jahren

Danke für die Antworten.
Da hatte ich offensichtlich den falschen Ansatz. Ich wollte erreichen, dass man die Methode EncodeDisc() aufruft und dann auf das Ergebnis wartet ohne das GUI zu blockieren.

5.299 Beiträge seit 2008
vor 15 Jahren

Tja,

warten == blockieren

(das ist im Grunde eine philosophische Erkenntnis)

Der frühe Apfel fängt den Wurm.

C
chavez Themenstarter:in
252 Beiträge seit 2007
vor 15 Jahren

Naja nicht ganz.
Ich habe das Encoding innerhalb meiner EncodeDisc Methode ja in einen eigenen Thread ausgelagert. Und durch die while Schleife(siehe meinen 1.Post) gewartet bis es fertig war. Durch die Application.DoEvents blockierte die Gui auch nicht.
Somit habe ich sozusagen ohne zu blockieren gewartet. 😉

Ich habe es übigens wie in diesem Artikel beschrieben mittels Subclassing Backgroundworker gelöst.
Das Encoding habe gleich in eine Klasse ausgelagert und diese erbt von Backgroundworker. Somit habe ich alle benötigten Features(GUI blockiert nicht und man wird über den Fortschritt des Encodens informiert)