Forum: PC-Programmierung c# - Threads und Process ein Verständnisporblem?


von Rahul D. (rahul)


Lesenswert?

Moin,
In einer C#-GUI wird extrerner Prozess gestartet, der einige Zeit 
braucht (Videokonvertierung á la ffmpeg - ähnlich dvdstyler) und somit 
die GUI blockiert,
Um dem Benutzer zu signalisieren, dass das Programm noch läuft, würde 
ich gerne einen Progressbar implementieren, und natürlich auch die 
Möglichkeit der Bedienung zu erhalten.

Jetzt stellt sich mir die Frage, wie ich den externen Prozess dazu 
bringe, den progessbar zu manipulieren.
Das OutputDataReceived-Event funktioniert soweit, dass ich mir die 
Statusinformationen des ffmepg auslesen und in einer Messagebox anzeigen 
lassen kann.

Muss ich den externen Prozess als eigenen Thread aufrufen bzw. mit Hilfe 
des Backgroundworkers laufen lasse?
Wie mache ich das?

Die von mir gefundenen Beispiele im Internet behandeln Threads alle sehr 
allgemein, und manupulieren immer nur Progressbars ohne einen externen 
Prozess.
Gäbe es ggf. noch eine andere Lösung?
Pribzipell könnte auch ein Fenster geöffnet werden, in dem es den 
Fortschrittsbalken und einn Knopf zum Abbrechen der Aktion vorhanden 
ist.
Dadürfte aber mMn wieder das threadübergreifende Zugriffsproblem 
bestehen.

Vielen Dank im Voraus!

von Peter II (Gast)


Lesenswert?

wie startest du denn das externe Programm? Das läuft normalerweise schon 
in einem Extra Thread - geht gar nicht anders, weil es ja an andere 
Programm ist.

Wenn du nicht gerade auf das Programm wartest, sollte deine GUI auch 
nicht blockieren.

von Rahul D. (rahul)


Lesenswert?

Peter II schrieb:
> Wenn du nicht gerade auf das Programm wartest, sollte deine GUI auch
> nicht blockieren.

Ich warte mit "WaitForExit()" auf das Ende des extewrnen Prozesses, weil 
danach noch mehr im Programmablauf kommt.
Prinzipiell könnte ich das Programm auch so laufen lassen, was aber die 
Benutzer irritieren würde (Sachen, die keine Reaktion zeigen 
funktionieren nicht, obwohl sie es eigentlich tun...)

von Peter II (Gast)


Lesenswert?

Rahul Der trollige schrieb:
> Ich warte mit "WaitForExit()" auf das Ende des extewrnen Prozesses, weil
> danach noch mehr im Programmablauf kommt.

dann ist das doch das Problem.

vewendet doch einfach das Event  Exited.

von Rahul D. (rahul)


Lesenswert?

Und

Peter II schrieb:
> dann ist das doch das Problem.
und wie bekomme ich dann den Progressbar aktualisiert?

>
> vewendet doch einfach das Event  Exited.

Müsste ich dann nicht für jeden Programmpunkt ein eigenes Event 
einrichten und es per throw auslösen?

von Peter II (Gast)


Lesenswert?

Rahul Der trollige schrieb:
> und wie bekomme ich dann den Progressbar aktualisiert?

das mach doch das OutputDataReceived Event.


> Müsste ich dann nicht für jeden Programmpunkt ein eigenes Event
> einrichten und es per throw auslösen?
was meist du damit?

was hat throw mit Events zu tun?

von Frieder S. (frsc)


Lesenswert?

Schau dir mal den BackgroundWorker an. Soweit ich weiß bringt der alle 
Funktionen mit, die du brauchst. Die Konvertierung findet dann in der 
DoWork-Methode statt.

von Peter II (Gast)


Lesenswert?

Frieder S. schrieb:
> Schau dir mal den BackgroundWorker an. Soweit ich weiß bringt der alle
> Funktionen mit, die du brauchst. Die Konvertierung findet dann in der
> DoWork-Methode statt.

hat er doch schon ein externen process - wozu dann noch ein thread 
ringsrum machen?

Er darf nur nicht auf das Ende vom Process warten, sondern muss auf das 
Event reagieren.

von Rahul D. (rahul)


Lesenswert?

Peter II schrieb:
> das mach doch das OutputDataReceived Event.

Das meckert, dass der progressbar einem anderen Thread gehört (sonst 
hätte ich auch gar nicht erst die Frage gestellt).

Müsste ich
Peter II schrieb:
> was meist du damit?
im weiteren Verlauf des Programms werden weitere externe Prozesse 
gestartet, die vom jeweils vorhergehenden abhängig sind.
Im Normalfall würden die hintereinander aufgerufen werden.
Wie soll ich das jetzt steuern? Bis jetzt hatte ich noch nie das 
Problem, dass ich auf externe Prozesse warten musste.

>
> was hat throw mit Events zu tun?

ich bin einfach ratlos und am Rumstochern...

von jibi (Gast)


Lesenswert?

>Das meckert, dass der progressbar einem anderen Thread gehört (sonst
>hätte ich auch gar nicht erst die Frage gestellt).

Invoke ist hier das Stichwort

von jibi (Gast)


Lesenswert?

Mal als Pseudocode:

starte 1 Externen Process
solange Process nicht fertig
    frage Process Status ab
    übernehme (invoke ) progressbar thread
    ändere progressbar.value entsprechend des process fortschrittes
starte 2 Externen Process

An Welcher stelle hängts?

von Rahul D. (rahul)


Lesenswert?

jibi schrieb:
> Mal als Pseudocode:

Danke.

jibi schrieb:
> übernehme (invoke ) progressbar thread

hier hakt es

von jibi (Gast)


Lesenswert?

Naja das Prinzip/Syntaxs eines "invokes" ist eigentlich nicht schwer  zu 
verstehen: Da threads nur auf sich selbst und ihre Kinder direkt 
zugreifen dürfen musst du in deinem Fall dem "fremden" thread einmal 
mitteilen das du gerne auf ihn zugreifen möchtest (genauer gesagt auf 
ein element das in ihm gespeichert hast) und da der sofortige zugriff 
nicht gewährt werden kann (da der thread vielleicht gerade was macht was 
nicht unterbrochen werden darf) musst du eine callback methode beim 
aufruf des invokes mit angeben - welche dann aufgerufen wird sobald der 
thread bereit dafür ist. in dieser callback methode hast du dann zugriff 
auf deine progressbar.
Beispiele dafür findet man zu hauf.
gruß Jonas

von jibi (Gast)


Lesenswert?


von Peter II (Gast)


Lesenswert?

jibi schrieb:
> Naja das Prinzip/Syntaxs eines "invokes" ist eigentlich nicht schwer  zu
> verstehen: Da threads nur auf sich selbst und ihre Kinder direkt
> zugreifen dürfen musst du in deinem Fall dem "fremden" thread einmal
> mitteilen das du gerne auf ihn zugreifen möchtest (genauer gesagt auf
> ein element das in ihm gespeichert hast) und da der sofortige zugriff
> nicht gewährt werden kann (da der thread vielleicht gerade was macht was
> nicht unterbrochen werden darf) musst du eine callback methode beim
> aufruf des invokes mit angeben - welche dann aufgerufen wird sobald der
> thread bereit dafür ist. in dieser callback methode hast du dann zugriff
> auf deine progressbar.
> Beispiele dafür findet man zu hauf.
> gruß Jonas

Dann hast es scheinbar nicht verstanden.

ob ein Thread auf einen anderen zugreifen kann hat nicht mit Invoke zu 
tun, sondern mit Threadsicherheit. Du kannst jederzeit auf jedes Element 
in einem anderen Thread zugreifen. Ein Objekt hat keine wirkliche 
Zugehörigkeit zu einen Thread es ist nur ein teil vom Ram vom Process.

Invoke wird gebraucht, weil alle GUI Element von den Hauptthread 
bearbeitet werden sollen. Mit Invoke übergibt man die Aufgabe über eine 
Windowsmessage den Hauptthread, dieser kümmern sich dann um die GUI.

von jibi (Gast)


Lesenswert?

>Da threads nur auf sich selbst und ihre Kinder direkt
>> zugreifen dürfen

Ich hab doch geschrieben dürfen, nicht können.

>Du kannst jederzeit auf jedes Element
>in einem anderen Thread zugreifen.

Eben nicht.

>Ein Objekt hat keine wirkliche
>Zugehörigkeit zu einen Thread es ist nur ein teil vom Ram vom Process.

Du hast schon gemerkt das ich es versucht hab vereinfacht darzustellen, 
was fängst du jetzt an mit nur ein Teil vom Ram vom Process??? ASLR 
lässt grüßen...

>Mit Invoke übergibt man die Aufgabe über eine
>Windowsmessage den Hauptthread, dieser kümmern sich dann um die GUI.

Warum hilfst du nicht dem Op statt hier Dinge zu erzählen die eh jeder 
weiss?

von Peter II (Gast)


Lesenswert?

jibi schrieb:
> Du hast schon gemerkt das ich es versucht hab vereinfacht darzustellen,
> was fängst du jetzt an mit nur ein Teil vom Ram vom Process??? ASLR
> lässt grüßen...

kann musst du es richtig sagen, es geht nur im GUI Elemente die diese 
"Eigenart" haben. Um nichts anders.

>Da threads nur auf sich selbst und ihre Kinder direkt

was sollen Kinder von Threads sein, Threads haben kein Parent.

von jibi (Gast)


Lesenswert?

was sollen Kinder von Threads sein?

Na Threads(Kinder) die gestartet werden in einem Thread (Eltern)...

Wenn es für dich so ein Klags ist dann hau im doch schnell die paar 
Zeilen Code hier hin.

gruß

von Rahul D. (rahul)


Lesenswert?

Hier mal der betreffende Code:
1
        // Prozess-Aktivitäten
2
        public delegate void updateProgressbarDeligate();
3
        public updateProgressbarDeligate updateProgressbar;
4
        private void StartProcess(string ProcName, string Arguments)
5
        {
6
            using (StreamWriter writer = new StreamWriter(exportpath + tempexportpath + "\\log.txt", true))
7
            {
8
                writer.Write("Prozess gestartet: ");
9
                writer.WriteLine(ProcName + Arguments);
10
                writer.WriteLine("");
11
            }
12
13
            try
14
            {
15
                ProcessStartInfo psi = new ProcessStartInfo(ProcName, Arguments);
16
                psi = new ProcessStartInfo(ProcName, Arguments);
17
                psi.RedirectStandardError = true;
18
                psi.RedirectStandardOutput = false;
19
                psi.UseShellExecute = false;
20
                psi.CreateNoWindow = true;
21
                Process p = new Process();
22
23
                //p.OutputDataReceived +=
24
                p.ErrorDataReceived +=
25
                    new DataReceivedEventHandler(p_OutputDataReceived);
26
                p.StartInfo = psi;
27
                p.Start();
28
29
                p.BeginErrorReadLine();
30
31
                p.WaitForExit(); // wie sieht die Alternative aus?
32
33
                p.Close();
34
                p.Dispose();
35
            }
36
            catch (NullReferenceException ne)
37
            {
38
                using (StreamWriter writer = new StreamWriter(exportpath + tempexportpath + "\\log.txt", true))
39
                {
40
                    writer.WriteLine(ne.StackTrace);
41
                }
42
            }
43
        }
44
        private void Processfile()
45
        {
46
            try 
47
            {
48
                this.Invoke(new updateProgressbarDeligate(updateProgressbarMethod));
49
            }
50
            catch { MessageBox.Show("Kaputt!"); }
51
        }
52
        void p_OutputDataReceived(object sender, DataReceivedEventArgs e)
53
        {        // Daten, die vom Prozess kommen, werden hier behandelt (funktioniert noch nicht optimal)
54
            try
55
            {
56
                if (e.Data != null)
57
                {
58
                    // Behandlung Progressbar
59
                    if (e.Data.IndexOf("frame=") > -1)
60
                    {
61
                        string FramesString = e.Data.ToString();
62
                        int FramesPos = FramesString.IndexOf("frame=") + 6;
63
                        int fpsPos = FramesString.IndexOf(" fps");
64
                        int FramesLength = fpsPos - FramesPos;
65
66
                        progressBarValue += 1; //= Convert.ToInt32(FramesString);
67
                        FramesString = e.Data.Substring(FramesPos, FramesLength);
68
                        MessageBox.Show(FramesString);
69
70
                        if (progressBar1.InvokeRequired)
71
                        {
72
                            // was muss hier rein?
73
                        }
74
                    }
75
                }
76
            }
77
            catch (NullReferenceException ne)
78
            {
79
                using (StreamWriter writer = new StreamWriter(exportpath + tempexportpath + "\\log.txt", true))
80
                {
81
                    writer.WriteLine(ne.StackTrace);
82
                }
83
            }
84
            using (StreamWriter writer = new StreamWriter(exportpath + tempexportpath + "\\log.txt", true))
85
            {
86
                writer.WriteLine(e.Data);
87
            }
88
89
        }

Ich bin wohl bzgl. Delegates, Invoke und Process-Threads etwas 
begriffsstutzig.
In p_OutputDataReceived bekomme ich ganz wunderbar die aktuelle 
Frame-Zahl ausgelesen und würde die nun gerne im Progressbar (, einer 
Textbox o.dergl.) anzeigen lassen.
Dass p_OutputDataReceived angesprungen wird, lasse ich mir durch eine 
Messagebox anzeigen.
Im Debugger sehe ich auch, dass die if-Abfrage 
"progressBar1.Invokerequired" durchgeführt und der Code in den Klammern 
ausgeführt wird.
Dort müsste ja auch eigentlich das Progressbar-Update erfolgen - nur 
wie?
Vielen Dank für die Unterstützung.

von Rahul D. (rahul)


Lesenswert?

Ich hab's übrigens hinbekommen:
statt "p.WaitForExit();":
1
                while (!p.HasExited)
2
                {
3
                    progressBar1.Value = (Task*100) +  (progressBarValue*100/frames);
4
                    this.Text = Convert.ToString(((Task * 100) + (progressBarValue * 100 / frames)) / 4) + "% Fortschritt";
5
                    Application.DoEvents();
6
                    //this.Refresh();
7
                }

Nebenbei wird auch noch der Fortschritt in der Titelzeile angezeigt.
application.DoEvent ist zwar gefährlich, aber bei meiner Anwendung 
werden sämtliche Eingabefelder gesperrt (außer dem "X" oben rechts).
Die Berechnung für den Progressbar hängt damit zusammen, dass dessen 
Maximumwert 400 ist und ich (momentan) vier Aufgaben im Ablauf habe - es 
werden aber noch mehr.

von bluppdidupp (Gast)


Lesenswert?


von Rahul D. (rahul)


Lesenswert?

bluppdidupp schrieb:
> http://stackoverflow.com/questions/2367718/automat...
>
> DoEvents() halte ich für Pfusch ;D

ist es ja auch, funktioniert aber in gewissen Grenzen... ;)

Auf welcher Basis das Programm funktioniert, ist dem "Kunden" doch egal, 
solange es einwandfrei funktioniert.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.