Autor |
Beitrag |
Rolo
Hält's aus hier
Beiträge: 7
|
Verfasst: Mo 02.07.07 11:34
Hallo zusammen!
Ich bin neu in C# unterwegs und benötige Unterstützung.
Problem:
Aus einem aufrufenden Thread wird ein zweiter gestartet. Dieser soll beim Schließen der Form zuerst geschlossen werden. Dabei bleibt die Anwendung stehen.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67:
| using System; using System.Windows.Forms; using System.Threading;
namespace thread_test { public partial class Form1 : Form { private volatile bool _shouldStop; private delegate void AddReceive(string msg); private Thread secondThread;
public Form1() { InitializeComponent();
secondThread = new Thread(new ThreadStart(Second)); secondThread.Name = "Second thread"; secondThread.Start(); }
public void Second() { int j = 0, i = 0; String msg;
while (!_shouldStop) { msg = Thread.CurrentThread.Name + ": working..." + j++; this.Invoke(new AddReceive(_AddReceive), new object[] { msg });
Thread.Sleep(1); }
while (i < 3) { msg = Thread.CurrentThread.Name + ": the last..." + i++; this.Invoke(new AddReceive(_AddReceive), new object[] { msg }); } } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { _shouldStop = true; secondThread.Join(); }
private void _AddReceive(string msg) { richTextBox1.AppendText(msg + "\n"); richTextBox1.ScrollToCaret(); }
private void button1_Click(object sender, EventArgs e) { Form1.ActiveForm.Close(); } } } |
Hat jemand eine Idee?
Gruß
Rolo
Moderiert von Christian S.: C#-Tags hinzugefügt
Einloggen, um Attachments anzusehen!
|
|
Kha
Beiträge: 3803
Erhaltene Danke: 176
Arch Linux
Python, C, C++ (vim)
|
Verfasst: Mo 02.07.07 13:21
Threading gehört nicht gerade zu meinem Fachgebiet, aber du könntest den Thread an seinem Ende ein Event feuern lassen und der Form verbieten, vor diesem Event zu terminieren.
[edit]Da du Thread und Oberfläche sowieso nicht trennst, könntest du statt dem Event natürlich auch gleich eine Methode der Form aufrufen. [/edit]
|
|
Rolo
Hält's aus hier
Beiträge: 7
|
Verfasst: Mo 02.07.07 14:36
Khabarakh hat folgendes geschrieben: | Threading gehört nicht gerade zu meinem Fachgebiet, aber du könntest den Thread an seinem Ende ein Event feuern lassen und der Form verbieten, vor diesem Event zu terminieren.
[edit]Da du Thread und Oberfläche sowieso nicht trennst, könntest du statt dem Event natürlich auch gleich eine Methode der Form aufrufen. [/edit] |
Die Form terminiert überhaupt nicht.
Wenn ich in der zweiten while-Schleife
"this.Invoke(new AddReceive(_AddReceive), new object[] { msg });"
durch
"Console.WriteLine(msg);"
ersetzte, terminiert die Form manchmal, manchmal auch nicht.
Lasse ich
"secondThread.Join();"
weg, das dafür sorgen sollte, daß der "Second thread" erst abgearbeitet, terminiert und dann die Form geschlossen wird, gibt es nach Rüchkehr aus dem Debugger die Meldungen
"Auf das verworfene Objekt kann nicht zugegriffen werden."
und
"System.Threading.ThreadAbortException".
Thread und Oberfläche sind doch getrennt??? Welche Methode sollte ausgeführt werden? Eine noch zu schreibende???
Gruß
Rolo
|
|
Kha
Beiträge: 3803
Erhaltene Danke: 176
Arch Linux
Python, C, C++ (vim)
|
Verfasst: Mo 02.07.07 16:31
Bis jetzt bin ich davon ausgegangen, dass du auf den Thread warten willst, um alle Ausgaben zu sehen. Geht es allerdings nur um die Exception, würde ich Folgendes vorschlagen: Du fügst den Thread-Code in eine eigene Klasse ein und spendierst dieser ein public event Action<string> Logging; An dieses hängst du in der Form einen Eventhandler, der den "this.Invoke..."-Aufruf, der sich zuvor im Thread befunden hat, enthält. Beim Schließen des Forms entfernst du diesen Eventhandler wieder, womit dein Thread völlig losgelöst ist und keinen Schaden mehr anrichten kann. Das wäre dann übrigens auch eine eindeutige Trennung von Daten und Anzeige: der Thread weiß nix vom Form.
|
|
Rolo
Hält's aus hier
Beiträge: 7
|
Verfasst: Mo 02.07.07 16:57
Khabarakh hat folgendes geschrieben: | Bis jetzt bin ich davon ausgegangen, dass du auf den Thread warten willst, um alle Ausgaben zu sehen. Geht es allerdings nur um die Exception, würde ich Folgendes vorschlagen:
Du fügst den Thread-Code in eine eigene Klasse ein und spendierst dieser ein public event Action<string> Logging; An dieses hängst du in der Form einen Eventhandler, der den "this.Invoke..."-Aufruf, der sich zuvor im Thread befunden hat, enthält. Beim Schließen des Forms entfernst du diesen Eventhandler wieder, womit dein Thread völlig losgelöst ist und keinen Schaden mehr anrichten kann.
Das wäre dann übrigens auch eine eindeutige Trennung von Daten und Anzeige: der Thread weiß nix vom Form. |
Da hab' ich mich wohl falsch ausgedrückt. Ich will schon alle Ausgaben aus dem "Second thread" sehen. Nur wennn der "this.Invoke"-Aufruf der zweiten while Schleife angezogen wird (manchmal auch der ersten, hängt wohl davon ab, wo die Abarbeitung der ersten while-Schleife gerade ist, wenn man den Close-Button drückt), dann geht nichts mehr, die Form bleibt stehen und akzeptiert keinerlei Eingaben mehr.
|
|
Kha
Beiträge: 3803
Erhaltene Danke: 176
Arch Linux
Python, C, C++ (vim)
|
Verfasst: Mo 02.07.07 18:37
Dann eben doch meine erste Idee: Füge ein Bool-Feld "running" ein, das du am Anfang des Threads auf true setzt. Am Ende setzt du es wieder auf false und invokest dann Close() des Forms. Im Closing-Eventhandler lässt du die Form nur dann terminieren, wenn running false ist (Thread.Join() natürlich entfernen).
|
|
Rolo
Hält's aus hier
Beiträge: 7
|
Verfasst: Di 03.07.07 09:17
Khabarakh hat folgendes geschrieben: | Dann eben doch meine erste Idee: Füge ein Bool-Feld "running" ein, das du am Anfang des Threads auf true setzt. Am Ende setzt du es wieder auf false und invokest dann Close() des Forms. Im Closing-Eventhandler lässt du die Form nur dann terminieren, wenn running false ist (Thread.Join() natürlich entfernen). |
Das tut nicht, denn wenn er
"this.Invoke(new AddReceive(_AddReceive), new object[] { msg });"
in der zweiten while-Schleife ausführt, hängt er sich schon auf.
|
|
Kha
Beiträge: 3803
Erhaltene Danke: 176
Arch Linux
Python, C, C++ (vim)
|
Verfasst: Di 03.07.07 17:48
"Hängt sich auf"? Meinst du die ObjectDisposedException vom ersten Versuch? Genau deswegen sollst du doch das Bool-Flag erst auf true setzen, wenn beide Schleifen durchlaufen sind und davor jeglichen Dispose-Versuch der Form stoppen, indem du im FormClosing-Eventhandler Cancel auf true setzt.
|
|
Rolo
Hält's aus hier
Beiträge: 7
|
Verfasst: Mi 04.07.07 10:53
Khabarakh hat folgendes geschrieben: | "Hängt sich auf"? Meinst du die ObjectDisposedException vom ersten Versuch? Genau deswegen sollst du doch das Bool-Flag erst auf true setzen, wenn beide Schleifen durchlaufen sind und davor jeglichen Dispose-Versuch der Form stoppen, indem du im FormClosing-Eventhandler Cancel auf true setzt. |
Das Problem ist, daß sich das Form aufhängt, sobald in der zweiten Schleife
"this.Invoke(new AddReceive(_AddReceive), new object[] { msg });"
aufgerufen wird, d. h. eine MessageBox-Ausgabe davor kommt noch, danach nicht mehr. Das Form akzeptiert keinerlei Eingaben mehr. Kann es sein, daß das Invoke auf den GUI-Thread wartet und der ja durch das Join blockiert ist und somit jeder Thread auf den anderen wartet???
|
|
Kha
Beiträge: 3803
Erhaltene Danke: 176
Arch Linux
Python, C, C++ (vim)
|
Verfasst: Mi 04.07.07 17:40
Wir diskutieren hier aneinander vorbei, denn du hast meine Idee anscheinend gar nicht umgesetzt - schließlich gibt es in dieser überhaupt kein Thread.Join(), wie ich geschrieben habe.
Dann eben hier einmal eine Umsetzung, allerdings etwas kryptisiert .
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42:
| namespace ThreadNonBlockingJoin { delegate void NullAction();
public partial class Form1 : Form { volatile bool running = true;
public Form1() { InitializeComponent(); }
void Log(string s) { textBox1.AppendText(s + Environment.NewLine); }
private void Form1_FormClosing(object sender, FormClosingEventArgs e) { e.Cancel = running; }
private void Form1_Load(object sender, EventArgs e) { ((NullAction)delegate { for (int i = 0; i < 10; i++) { Invoke((Action<string>)Log, i.ToString()); Thread.Sleep(1000); if (!running) break; } running = false; }).BeginInvoke(delegate { Invoke((NullAction)Close); }, null); } } } |
|
|
Rolo
Hält's aus hier
Beiträge: 7
|
Verfasst: Do 05.07.07 09:10
[quote=" Khabarakh"]Wir diskutieren hier aneinander vorbei, denn du hast meine Idee anscheinend gar nicht umgesetzt - schließlich gibt es in dieser überhaupt kein Thread.Join(), wie ich geschrieben habe.
Sorry! Das "kryptisierte" unter "Form1_Load" verstehe ich leider nicht mehr.
|
|
Kha
Beiträge: 3803
Erhaltene Danke: 176
Arch Linux
Python, C, C++ (vim)
|
Verfasst: Do 05.07.07 18:06
Du kannst einen Thread nicht nur über die gleichnamige Klasse, sondern auch über jeden Delegate durch die Methode BeginInvoke erstellen. Das ganze habe ich dann noch per anonymous methods doppeltgemoppelt gekoppelt ^^ . Aber wie gesagt, Threading ist nicht mein Fachgebiet, also habe ich gleich mal vergessen, den Thread auch wieder ordnungsgemäß zu schließen . Hier die Korrektur, die zusätzlich etwas verständlicher sein sollte.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55:
| delegate void NullAction();
public partial class Form1 : Form { volatile bool running;
public Form1() { InitializeComponent(); }
void Log(string s) { textBox1.AppendText(s + Environment.NewLine); }
void ParallelizeMe() { running = true; try { for (int i = 0; i < 10; i++) { Invoke((Action<string>)Log, i.ToString()); Thread.Sleep(1000); if (!running) break; } } finally { running = false; } }
void MainformClosing(object sender, FormClosingEventArgs e) { e.Cancel = running; }
void Form1_Load(object sender, EventArgs e) { NullAction threadMethod = ParallelizeMe;
threadMethod.BeginInvoke(delegate(IAsyncResult result) { threadMethod.EndInvoke(result); Invoke((NullAction)Close); }, null); } } } |
|
|
Rolo
Hält's aus hier
Beiträge: 7
|
Verfasst: Fr 06.07.07 08:40
[quote=" Khabarakh"]Du kannst einen Thread nicht nur über die gleichnamige Klasse, sondern auch über jeden Delegate durch die Methode BeginInvoke erstellen. Das ganze habe ich dann noch per anonymous methods doppeltgemoppelt gekoppelt ^^ . Aber wie gesagt, Threading ist nicht mein Fachgebiet, also habe ich gleich mal vergessen, den Thread auch wieder ordnungsgemäß zu schließen . Hier die Korrektur, die zusätzlich etwas verständlicher sein sollte.
Danke für Deine Hilfe!
Ich muß das Ganze ersteinmal richtig verdauen.
|
|
|