Entwickler-Ecke
WinForms - GUI bleibt trotz Thread hängen
RBS2002 - Mi 24.11.10 11:53
Titel: GUI bleibt trotz Thread hängen
Hi,
ich habe folgendes "Schönheitsproblem". Ich stelle in meinem Programm eine Funktion zum Datendownload bereits (die Dateien kommen von einer Datenbank). Es funktioniert auch alles - nur bleibt die GUI, obwohl ich dem Thread alle benötigten Daten mitgebe und seperat starte, bis zum Ende des Speichervorgangs hängen, d.h. der Benutzer kann in der Zeit nichts machen.
Hier erst einmal der Code:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
| if (_lastmail != null) { var parameter = new object[4]; parameter[1] = attatchment.SelectedItems[0].Text.Split(' ')[0]; parameter[2] = Copy.CopyObject(_lastmail); parameter[3] = Copy.CopyObject(_eMail.Email);
var dialog = new SaveFileDialog { FileName = attatchment.SelectedItems[0].Text.Split(' ')[0] };
if (dialog.ShowDialog() == DialogResult.OK) { parameter[0] = dialog.FileName; var pts = new ParameterizedThreadStart(SaveFile); _thread = new Thread(pts); _thread.Start(parameter); } } |
Dies wird nach einem Doppelklich aufgerufen - die Daten holt er sich vom derzeitigen Objekt und der Attatchmentliste (das gerade ausgewählte Element).
Hier der Quellcode vom Thread:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| private void SaveFile(object path) { var newpath = (object[])path; string filepath = newpath[0].ToString(); string filename = newpath[1].ToString(); var mail = (Daten.Mail)newpath[2]; string email = newpath[3].ToString();
using (var outputStream = new FileStream(filepath, FileMode.Create, FileAccess.Write)) { var daten = (byte[]) _datenbank.Mysql.MailAction(mail, email, Konstanten.PostAusgang, filename, Konstanten.GetAttatchment); outputStream.Write(daten, 0, daten.Length); } } |
An was kann das liegen bzw. ist das eine Eigenart von C#? Würde mich über Hinweise,Tipps freuen ;)
Vielen Dank im Vorraus,
mfG RBS2002
PS: Falls die Frage auftaucht - das Programm wird, im Ernstfall, erst beendet nachdem der Download abgeschlossen wurde (auch das Fenster kann in der Zeit nicht gewechselt werden, aber das will ich noch ändern) - dazu habe ich ein paar Abfragen eingebaut die ich auf Grund der Platzersparniss nicht hingeschrieben habe :D
Th69 - Mi 24.11.10 13:00
Hallo,
so auf den ersten Blick sollte deine Methode einwandfrei funktionieren.
Interagiert evtl. die _datenbank.Mysql.MailAction()-Methode mit der GUI?
Du könntest einfach mal während die GUI hängt den Debugger anhalten und schauen in welcher Methode der MainThread hängt (Fenster "Threads"). Dies sollte normalerweise einfach "Application.Run()" sein.
Trashkid2000 - Mi 24.11.10 13:06
Hallo,
also ich würde auch sagen, dass es nicht an dem Codeschnipel liegt. Habe es mal mit einer Pseudomethode probiert. Der SaveFileDialog schließt sich sofort nach dem drücken von OK, und der Thread wird gestartet. Die Form ist während der Wartezeit auch nicht blockiert. Das Blockieren muss wohl woanders hervorgerufen werden.
Aber was mir aufgefallen ist:
Du geht ziemlich gelassen mit Indexen um. Und das ohne vorherige Prüfung des Objektes auf die Länge. Da könnte es und an mal knallen, wenn der Index mal nicht existiert!
Marko
RBS2002 - Mi 24.11.10 14:05
ja - ich habe mich da etwas zu sehr auf die _lastmail Eigenschaft verlassen (und das beim Doppelklick immer was ausgewählt werden müsste, aber die Liste kann ja auch leer sein - das ist richtig) und ich werde das verbessern.
Ansonsten müsste ich wirklich mal schauen woran das liegt - es kommt auch nur beim abspeichern der Datei vor, sprich wenn der Button mit ok bestätigt wurde. Aber hier mal meine SQL Funktion - nicht das es daran liegt, obwohl dort eigentlich nichts mit der GUI gemacht wird bzw. darauf zugegriffen wird. (es fehlt jetzt natürlich das switch, ansonsten ist es der Code der aufgerufen wird)
C#-Quelltext
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:
| MySqlConnection connection = null; postfach = postfach.Replace("@", "_").Replace(".", "_"); case Konstanten.GetAttatchment: if (Verbindung(out connection)) { try { lock (connection) { string datum = mail.Datum.Year + "-" + mail.Datum.Month + "-" + mail.Datum.Day + " " + mail.Datum.Hour + ":" + mail.Datum.Minute + ":" + mail.Datum.Second;
string sql = "SELECT file,file_size FROM " + _datenbank.Prefix + postfach + "_" + Konstanten.DatenAttatchment + " WHERE typ='" + mailtyp + "' " + "AND datum='" + datum + "'" + "AND file_name='" + filename + "'" + "AND sender='" + mail.Von + "'" + "AND receiver='" + mail.To + "'" + "AND betreff='" + mail.Betreff + "'";
var myCommand = new MySqlCommand { Connection = connection, CommandText = sql }; MySqlDataReader reader = myCommand.ExecuteReader(); while (reader.Read()) { Int32 filezise = reader.GetInt32(reader.GetOrdinal("file_size")); var rawData = new byte[filezise]; reader.GetBytes(reader.GetOrdinal("file"), 0, rawData, 0, filezise); return rawData; } return null; } } catch (Exception ex) { MessageBox.Show(ex.Message + @": " + ex.StackTrace, @"Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error); return null; } finally { Disconnect(connection); } } return null; |
Aber erst einmal vielen Dank für eure Hilfe.
Th69 - Mi 24.11.10 16:22
Das "lock (connection)" gefällt mir nicht so sehr. Könnte es sein, daß du in der GUI auch auf die Datenbank zugreifst (z.B. mittels eines Timers) und dort dann das selbe Connection-Objekt zum 'lock'en benutzt (das von "Verbindung(out connection)" zurückgegeben wird)?
Eine Connection sollte immer so lokal wie möglich sein, d.h. du solltest keine globale Connection verwenden. Am besten du zeigst mal deine Verbindung()-Methode.
RBS2002 - Mi 24.11.10 17:30
Das lock(connection) stammt noch von einer alten Architektur und ist im Prinzip nicht mehr nötig - theoretisch deswegen auch nicht schadhaft da jede Operation auf die Datenbank ihre eigene Connection hat (wird ja mehrmals aufgerufen - die lokale Connection die du oben siehst wird bei jeden Methodenaufruf neu erzeugt).
Aber hier mal die Connect-Methode:
C#-Quelltext
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:
| private Boolean Verbindung(out MySqlConnection connection) { connection = null; { if (_datenbank != null) { connection = new MySqlConnection("SERVER=" + _datenbank.Server + ";" + "PORT=" + _datenbank.Port + ";" + "DATABASE=" + _datenbank.Name + ";" + "UID=" + _datenbank.Benutzername + ";" + "PASSWORD=" + _datenbank.Password + ";"); try { connection.Open(); return true; } catch (Exception e) { MessageBox.Show(e.Message + e.StackTrace); return false; } } MessageBox.Show(@"Datenbank ist null"); return false; } } |
und die Disconnect-methode:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16:
| public Boolean Disconnect(MySqlConnection connection) { if (connection != null) { try { connection.Close(); return true; } catch (Exception) { return false; } } return true; } |
Theoretisch könnte es sogar an den Execute liegen - aber dies wird ja auch vom Thread aufgerufen und läuft dadurch, eigentlich, unabhängig von der GUI. Momentan glaube ich es liegt an meinen PC :D
Kha - Mi 24.11.10 23:12
Hast du denn nun schon Th69s Vorschlag mit dem Debugger ausprobiert? Sowohl Locks als auch Invokes sollten da sofort entlarvt werden können.
RBS2002 - Mi 24.11.10 23:22
Ich werde das morgen mal machen und für den Fall gleich mal das lock rausnehmen (da es, wie gesagt, eh irrelevant ist) - aber für mein logisches Verständnis ist das mit den Locks widersinnig da jeder noch so kleine Aufruf eine eigene Connection besitzt, d.h. selbst wenn 2 Threads gleichzeitig das gleiche machen und die Funktion zweimal aufgerufen würde hätte jeder seine eigene Connection ==> Lock kann sofort betreten werden da es keinen Streit gibt. Oder ich verstehe etwas grundsätzliches beim switch-case oder beim C# lock falsch.... Naja, mal sehen. Aber erst einmal danke für die Hilfe. Sonst hätte ich ja schon durch die Disconnects ein Problem - dann würde der eine ein Disconnect machen und der andere will noch darauf zugreifen. Das Problem hatte ich am Anfang als ich wirklich einen Klassenparameter für alle erstellt habe - deswegen auch noch das Lock was durch das Umschreiben eigentlich egalisiert sein müsste - oder sehe ich das falsch?
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!