Einzelnen Beitrag anzeigen

berens

Registriert seit: 3. Sep 2004
431 Beiträge
 
Delphi 2010 Professional
 
#19

AW: Exception ohne wirklichen Auslöser treibt mich in den Wahnsinn!

  Alt 20. Aug 2013, 19:12
Genau das ist ja das, was mich die ganze Zeit stutzig gemacht hat: Eine Prozedur im Hauptthread löst eine Exception mit Objekten aus dem Thread aus. Vollkommen unsinnig... Wäre da nicht... Das gute alte Copy&Paste, wenn man einfach 1:1 Sachen aus seiner Technik-Demo in das fertige Programm übernimmt:

Beim gezielten Suchen nach dem Hauptformular habe ich festgestellt, dass nicht nur in meiner Thread-Unit, sonder direkt in der Thread.Execute ein verweis auf das VCL-Hauptformular frmMain ist. Und das natürlich noch an bester Stelle:

Delphi-Quellcode:
            tmpBestellung := TBestellung.Create(frmMain);
            with tmpBestellung do begin
              ID := q.FieldByName('ID').AsInteger;
              refProjekt := q.FieldByName('refProjekt').AsInteger;
              dtBestellung := q.FieldByName('dtBestellung').AsDateTime;
              dtAenderung := q.FieldByName('dtAenderung').AsDateTime;

              LastUpdate := CurrentTick;
            end;
Das gehört in einem Thread natürlich auf keinen Fall so (wie gesagt: ich habe es damals halt erst alles naträglich in einen Thread umgeschrieben ).

Eine kleine Änderung in:
tmpBestellung := TBestellung.Create(NIL); Und ... oh Wunder der Technik: aktuell läuft es.

Was mich nun wieder speziell an meine "Regeln" oben erinnert: Was zur Hölle schafft Delphi/Windows mit dem "Owner" so krasses? Ich denke mal, dadurch, dass die (Thread-)Objekte in jedem Thread-Durchlauf neu erzeugt und dann wieder komplett gelöscht werden wird Aufgrund diesen Fehlers jedes Mal eine Nachricht an frmMain gesendet wie "Du besitzt nun ein neues Objekt, für dessen .Free du zuständig bist: tmpBestellung". Wobei dieses Objekt dann gleich wieder gelöscht wird, und das Senden dieser Nachricht an frmMain wahrscheinlich gleichermaßen ein Schwerverbrechen ist, wie direkt frmMain.Caption := 'Hallo' im Thread.Execute zu schreiben.

Uwe: Erklär mir doch bitte noch in 1-2 Sätzen, wie ich den Thread ohne Aufruf eines Synchronize abbrechen kann. Suspend pausiert ja nur, und lässt nicht wirklich ein sauberes Ende zu (Komponenten freigeben, AdoVerbindung ordentlich beenden etc. Soll ich z.B. eine Prozedur schreiben, in der ich über CriticalSection einfach z.B. eine Boolean-Variable setzt, und diese im Thread-Verlauf ganz normal auslese ("if not blStop then begin...")?

"Der schöne Günther": Die Felder meiner von TThread abgeleiteten Klasse.
Wenn ich Dich richtig verstehe, dürfen im Prinzip 1000 verschiedene Threads problemlos gleichzeitig tmpBestellung.ID auslesen, wenn diese irgendwie an das Objekt kommen. Was passiert, wenn dieses Objekt eine public-Variable/Objekt von frmMain ist, z.B. frmMain.AktuelleBestellung (nur als Beispiel!)?

Blup: "Execute" sollte eigentlich protected sein. Korrekt. Korrigiert.

Ich hoffe, dass das Projekt jetzt dauerhaft ordentlich läuft. Ich fahre jetzt alles wieder auf den alten Stand (auch OwnsObject und Freigabe der Objekte etc.) Somit war scheinbar alles mal wieder ein Fehler des Schlampigkeit. Trotzdem ein Rüffel an Delphi, irgendwo könnte ja auch stehen, dass z.B. der Fehler beim Zugriff auf die vom frmMain "ge-owned-en" Objekte (wie ist das Wort?), also die Objekte, deren Owner frmMain ist.

Tut mir wie immer Leid, Eure Arbeits- oder Freizeit stark in Anspruch genommen zu haben.
Ich danke vielmals für die geduldigen Antworten!

Anbei nochmal "meine" überarbeiteten Thread-Regeln, mit der Einladung an alle, die sie lesen, mich zu verbessern:
Speziell zu 8: Was passiert, wenn ich ein Object mit Owner = frmMain an den Thread übergebe, und dieser es löschen soll? Muss ja dann zwangläufig mit Synchronize erfolgen, auch wenn ich mit dem Hauptprogramm selber nie wieder schreibend auf das Objekt zugreifen will. Oder?

Darf ich in CriticalSection auf VCL-Objekte zugreifen? Ich muss mich da mal einlesen.

WICHTIGE Frage: Darf ich denn Variable, die garantiert nur vom Thread geschrieben werden (z.B. blTerminated) jederzeit ohne CriticalSelection schreiben, oder muss ich, weil ich mit frmMain im 10ms Takt "if LoadSave.blTerminated" beispielhaft überprüfe, ob der Thread fertig beendet ist unbedingt die CriticalSelection verwenden? frmMain ließt ja nur!?

Code:
1) Mit dem Main-Thread darf man niemals [I]schreibend [/I]auf Thread-Variablen oder Objekte zugreifen, die jemals im Thread.Execute verwendet werden

2) Im Thread.Execute darf niemals [I]schreibend [/I] auf externe Variablen oder Objekte zugegriffen werden

3) Im Thread.Execute dürfen keine VCL-Komponenten verwendet werden. Wenn irgendwo eine (speziell "Visuelle" - also sichtbare) Komponente referenziert wird (z.B. das Hauptformular), ist das zu 100% ein Fehler! Nicht-VCL-Objekte die z.B. unter "public" bei einer VCL-Komponente stehen, dürfen angesprochen werden(?)

4) Im Thread.Execute darf niemals auf Variablen des Thread-Objekts zugegriffen werden, die irgendwann mal von extern gesetzt oder gelesen werden sollen/können (z.B. public Variablen).

5) Im Thread verwendete Komponenten und Units müssen "Thread-Sicher" sein.

6) Regel 1, 2, 4 dürfen mit CriticalSection umgangen werden.

7) Objekte, die im Thread.Execute erstellt wurden (ohne Owner!) dürfen an das Hauptprogramm übergeben werden, sofern Thread.Execute nie wieder schreibend darauf zugreift.

8) In die andere Richtung: Im Hauptprogramm erstellte Objekte -speziell welche mit z.B. Owner=frmMain- dürfen den Thread.Execute übergeben werden, sofern nur gelesen wird.

9) ".Terminate" setzt einfach nur die Variable "Terminated" auf True. Ob der Thread wirklich beendet wurde, erfahre ich nur, wenn ich dafür eine eigene Variable setzte. (z.B. meine blTerminated) Diese Variable kann nur CriticalSelection oder Synchronize im Thread geschrieben werden, und kann/darf problemlos jederzeit vom Hauptprogramm ausgelesen werden. OnTerminate oder "Finished" ist eine bessere Lösung.

10) Es ist für TThread.Execute kein Unterschied, ob ich eine TComponentList in .Create oder .Execute erstelle.

Geändert von berens (20. Aug 2013 um 20:40 Uhr)
  Mit Zitat antworten Zitat