AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Object-Pascal / Delphi-Language Delphi Exception ohne wirklichen Auslöser treibt mich in den Wahnsinn!
Thema durchsuchen
Ansicht
Themen-Optionen

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

Ein Thema von berens · begonnen am 19. Aug 2013 · letzter Beitrag vom 20. Aug 2013
Antwort Antwort
Seite 2 von 3     12 3      
Benutzerbild von p80286
p80286

Registriert seit: 28. Apr 2008
Ort: Stolberg (Rhl)
6.659 Beiträge
 
FreePascal / Lazarus
 
#11

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

  Alt 20. Aug 2013, 11:24
Nochmal: Das komplette Programm läuft zuverlässig und einwandfrei. Das einzige was "stört" ist die Exception-Meldung ansich. Deshalb die leeren try..except Blöcke rund um allen Quelltext, den ich für den Bösewicht halte. Trotzdem kommt die Meldung weiterhin.
"Mein Auto läuft einwandfrei, nur dieses Lämpchen, was aussieht wie eine Ölkanne, das stört. Wie kann man das abschalten.

Das ist die gleiche Logik!
Aber bitte mach ruhig.

Gruß
K-H
Programme gehorchen nicht Deinen Absichten sondern Deinen Anweisungen
R.E.D retired error detector
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.475 Beiträge
 
Delphi 12 Athens
 
#12

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

  Alt 20. Aug 2013, 11:27
Zeig doch mal die Deklarationen und Instanzierungen der TComponentList-Instanzen.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
berens

Registriert seit: 3. Sep 2004
434 Beiträge
 
Delphi 10.4 Sydney
 
#13

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

  Alt 20. Aug 2013, 11:40
In dem Execute vom Thread habe ich ja auch Protokoll-Funktion (wenn auch zunächst mal nur für Debugger), da dort das try..except nicht greift, habe ich die einzelnen Blöcke mit dem blanko try..except umgeben, um zu schauen, ob die Meldung nun verschindet, bzw. dort z.B. ein Sleep(0) gesetzt, um dort mit einem Haltepunkt anzuhalten. Irgendwie muss ich den Fehler ja eingrenzen.

Bei den TComponentListe hatte ich vor den Tests auch welche mit OwnsObjects=True dabei (wegen Garbage-Collection uns Leaks), das ändert allerdings nix an der Problematik.

Delphi-Quellcode:
  TLoadSaveThread = class(TThread)
  private
    FPanicLevel: integer;
    blStop, FblStop: Boolean;
    FslBenutzer: TStringList;

    FclAlleVorgaenge: TComponentList;
    FclBestellDetail: TComponentList;
    FclLebensStationen: TComponentList;

    FclProjekte: TComponentList;
    FclKontakte: TComponentList;
    FclWiederVorlage: TComponentList;
    FclTicket: TComponentList;
    FclMail: TComponentList;
    FclBestellung: TComponentList;
    FclArtikel: TComponentList;

    FclSave_Inbox: TComponentList;
    FclSave_Thread: TComponentList;
    FclSave_Outbox: TComponentList;

    FclDelete_Inbox: TComponentList;
    FclDelete_Thread: TComponentList;
    FclDelete_Outbox: TComponentList;

    FNewDataSet: TLoadSaveItem;
    FProgressText: string;
    procedure SyncSaveAndDelete;
    procedure SyncNewDataset;
    procedure SyncFinished;

    procedure SyncProjekte;
    procedure SyncKontakte;
    procedure SyncWiederVorlage;
    procedure SyncTickets;
    procedure SyncMails;
    procedure SyncBestellung;
    procedure SyncBestellDetails;
    procedure SyncVorgang;
    procedure SyncArtikel;


    procedure SyncStop;
    procedure SyncBenutzer;
    procedure SyncAblaufschritte;
    procedure SyncProgress;

  public
    clBestellDetail: TComponentList;
    clProjekte: TComponentList;
    clKontakte: TComponentList;
    clWiederVorlage: TComponentList;
    clTicket: TComponentList;
    clMail: TComponentList;
    clBestellung: TComponentList;
    clArtikel: TComponentList;

    clAlleVorgaenge: TComponentList;

    blTerminated: Boolean;
    slBenutzer: TStringList;
    ConnectionString: string;

    OnChange: TNotifyEvent;
    OnProgress: TNotifyEvent;
    ProgressText: string;
    PanicLevel: integer;

    ToDo: integer;

    procedure Execute; override;
    procedure Stop;

    procedure Delete(_LoadSaveItem: TLoadSaveItem);
    procedure Save(_LoadSaveItem: TLoadSaveItem);
    procedure SyncAlles;

    constructor Create(_Suspended: Boolean); reintroduce;
    destructor Destroy; override;
  end;


constructor TLoadSaveThread.Create(_Suspended: Boolean);
begin
  inherited;

  try
    OnChange := NIL;
    FblStop := False;

    FclSave_Inbox := TComponentList.Create(False);
    FclSave_Thread := TComponentList.Create(False);
    FclSave_Outbox := TComponentList.Create(False);

    FclDelete_Inbox := TComponentList.Create(False);
    FclDelete_Thread := TComponentList.Create(False);
    FclDelete_Outbox := TComponentList.Create(False);

    FclProjekte := TComponentList.Create(False);
    clProjekte := TComponentList.Create(False);

    FclKontakte := TComponentList.Create(False);
    clKontakte := TComponentList.Create(False);

    FclWiederVorlage := TComponentList.Create(False);
    clWiederVorlage := TComponentList.Create(False);

    FclTicket := TComponentList.Create(False);
    clTicket := TComponentList.Create(False);

    FclMail := TComponentList.Create(False);
    clMail := TComponentList.Create(False);

    FclBestellung := TComponentList.Create(False);
    clBestellung := TComponentList.Create(False);

    FclBestellDetail := TComponentList.Create(False);
    clBestellDetail := TComponentList.Create(False);

    FclArtikel := TComponentList.Create(False);
    clArtikel := TComponentList.Create(False);

    clAlleVorgaenge := TComponentList.Create(False);

    slBenutzer := TStringList.Create;
    ToDo := 0;
    blTerminated := False;
  except end;
end;



procedure TLoadSaveThread.Execute;
var
  db: TADOConnection;
  q: TADOQuery;
  i: integer;
  ...
begin
  q := NIL;
  db := NIL;
  blBenutzerGeladen := False;
  blVorlagenGeladen := False;
  blMailsGeladen := False;
  blArtikelGeladen := False;
  try
    FProgressText := 'Starten ...';
    Synchronize(SyncProgress);

    CoInitialize(NIL);
    FslBenutzer := TStringList.Create;
    FclAlleVorgaenge := TComponentList.Create(True);
    FclLebensStationen := TComponentList.Create(True);

    FProgressText := 'Verbinde Datenbank ...';
    Synchronize(SyncProgress);
    db := TADOConnection.Create(NIL);
    db.LoginPrompt := False;
    db.ConnectionString := Self.ConnectionString;
    db.Connected := True;

    FProgressText := 'Erstelle Abfrage ...';
    Synchronize(SyncProgress);
    q := TADOQuery.Create(NIL);
    q.Connection := db;
  Mit Zitat antworten Zitat
Blup

Registriert seit: 7. Aug 2008
Ort: Brandenburg
1.477 Beiträge
 
Delphi 12 Athens
 
#14

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

  Alt 20. Aug 2013, 17:01
Exceptions in einem Thread zu debuggen ist zumindest problematisch.
Als Ursache kommt der Thread schon in Betracht, aber aufgetreten ist die Exception nach EurekaLog im Hauptthread.
Da nützen dies ganzen try..except-Blöcke wenig.

Aus den bisherigen Code-Schnipseln konnte ich keine Fehlerursache erkennen, aber einige Dinge sind schon aufgefallen:

Warum wird "OwnsObjects" bei allen Listen auf False gesetzt?
Irgendwer muss doch für das Freigeben der Objekte zuständig sein.

Es gibt verschiedene Variablen die sowohl private als auch public auftauchen:
Delphi-Quellcode:
private
  FPanicLevel: integer;
  FProgressText: string;
{...}
public
  ProgressText: string;
  PanicLevel: integer;
Da kann schnell mal was verwechselt werden.

"Execute" sollte eigentlich protected sein, nur der Thread selbst darf diese Methode aufrufen.

Delphi-Quellcode:
public
{...}
  blTerminated: Boolean;
  slBenutzer: TStringList;
  ConnectionString: string;
  OnChange: TNotifyEvent;
  OnProgress: TNotifyEvent;
{...}
Da es keine CriticalSection gibt, die den Zugriff auf öffentlich Properties schützt, darf der Zugriff darauf grundsätzlich nur im Hauptthread bzw. über Synchronize erfolgen.

Diese ganzen "public" Listen sollten eigentlich unabhängig vom Thread existieren.
Vieleicht eine unabhängige Klasse die alle Listen verwaltet und global bereitstellt.
Damit würde der Rest des Programms erst einmal von dieser Threadklasse entkoppelt.

Warum gibt es so viele verschiedene interne Listen?
Eigentlich wird doch immer nur eine Liste zur selben Zeit bearbeited.
Die Liste wird aus der Datenbank gelesen.
Dann im Synchronize mit einer öffentlichen Liste verarbeitet.
Welche das ist, ergibt sich aus dem Kontext.
Danach in die Datenbank geschrieben, der Inhalt gelöscht und steht wieder für die nächste Aufgabe zur Verfügung.

Man könnte überlegen für jede Aufgabe eine Klasse zu erstellen.
Die einzelnen Abgleiche unterscheiden sich im wesentlichen nur in:
- Name des Abgleichs
- SQL-Anweisung und Erstellen, Lesen und Speichern der Objekte
- der öffentlichen Liste für den Abgleich
Da lässt sich sicherlich eine Basisklasse bilden, die schon einen wesentlichen Teil einheitlich implementiert.
Alle Aufgaben in einer Liste die der Thread der Reihenfolge nach abarbeitet und schon sieht alles viel aufgeräumter aus.
Das lässt sich auch viel besser einzeln testen und erweitern.
  Mit Zitat antworten Zitat
berens

Registriert seit: 3. Sep 2004
434 Beiträge
 
Delphi 10.4 Sydney
 
#15

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

  Alt 20. Aug 2013, 17:45
Oben schonmal im Nebensatz: OwnsObject wurde zu Testzwecken überall mal auf "False" gesetzt, weil ich einfach schätzungsweise alles ausprobiert habe, den Fehler zu finden.

Alle doppelten Listen und Variablen verfolgen den Zweck, dass ich auf gar keinen Fall mit dem Mainthread auf Variablen/Objekte von diesem Thread zugreife.

Ich habe das Grundkonzept von Threads und "eiserne Regel", dass man vom Main-Thread nicht Objekte von (m)einem Thread zugreift (und andersrum) schon verstanden, aber ich glaube, ich setze das total falsch um.

Aus lauter Angst, versehentlich auf Variablen und Objekte zuzugreifen die gerade de andere Thread verwendet, habe ich immer alle Variablen und Objekte doppelt. Eine nur für den Main-Thread (z.B. clProjekte, public) und eine nur für den Thread (FclProjekte, private). Einzig in Synchronize(SyncBlabla) werden die Objekte von Thread nach Main kopiert und andersherum. Auch hier erstelle ich wieder ausschließlich neue Objekte, die mit Assign die Werte zugewiesen bekommen, vor lauter Sorge, wenn ich ein Objekt, das im Thread erstellt wurde aus einer TComponentList extrahierte (.Extract) und der ComponentList vom Main-Thread hinzufüge, dass dieses Objekt sich nicht 100% garantiert genau so verhält wie eines, das im Main-Thread erstellt wurde (vll. wegen Owner oder sonst irgendwelchen "Objektinternen"-Werten).

Ich werde mir CriticalSection mal genauer anschauen. Dies habe ich bisher noch nie verwendet, da ich davon ausgegangen bin, dass bei dem TThread-Objekt Synchronize() das empfohlende Pendant zu CriticalSection (Threads mit purer Api) ist.

Ich habe auch schon mehrere Thread-Tutorials durchgelesen. Haut mich, wenn ich einfach dabei gepennt habe, aber vielleicht sammeln wird hier gerade nochmal die wichtigsten Fakten, "DOs und DON'Ts".

Ich programmiere mit diesen Grundsätzen im Hinterkopf, bitte ergänzt oder korrigiert mich hier:
(Der Einfachheit halber nenne ich nachfolgend alle Objekte und Variablen etc. nur noch "Variablen".)

1) Mit dem Main-Thread darf man niemals auf Thread-Variablen zugreifen -weder lesend noch schreibend- die jemals im Thread.Execute verwendet werden

2) Im Thread.Execute darf niemals auf externe Variablen zugegriffen werden -weder lesend noch schreibend- (außerhalb des eigenen Objekts)

3) Im Thread.Execute dürfen keine VCL-Komponenten (speziell auch sichtbare Objekte) verwendet 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 evtl. mit CriticalSection umgangen werden???

7) Objekte, die im Thread.Execute erstellt wurden (mit oder ohne Owner) und mit Synchronize an das Hauptprogramm übergeben werden, lassen das Universum kollabieren. Dito in die andere Richtung: Im Hauptprogramm erstellte Objekte -speziell welche mit z.B. Owner=frmMain- dürfen auch mit Synchronize nicht an den Thread.Execute übergeben werden (also der direkte Pointer auf das Objekt), selbst wenn Synchronize die Referenz im MainThread auf dieses Objekt löscht), da der Thread -weshalb auch immer- z.B. auf den Owner zugreifen könnte und alles abstürzt.

8) ".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)

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


So, jetzt bekomme ich von Euch bestimmt gleich dieses o.g. Thread-Halb-Wissen um die Ohrengehauen. Lasset den Sturm beginnen.

Ne im Ernst, manchmal mach Delphi wirklich viel im Hintergrund und ich weiss nicht so recht, ob ich "das jetzt machen darf oder nicht". Mit dieser Arbeitsintensiven doppelt und dreifach Abspeicherei der Objekte will ich wirklich 100% sicher gehen, dass mein Programm nicht wegen dem Zugriff von Thread und Mainthread auf die selbe Variable abstürzt.
  Mit Zitat antworten Zitat
Der schöne Günther

Registriert seit: 6. Mär 2013
6.176 Beiträge
 
Delphi 10 Seattle Enterprise
 
#16

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

  Alt 20. Aug 2013, 18:10
Ich habe dieses Thema seit Bestehen auf meiner "Muss ich mir näher anschauen und nicht nur überfliegen"-Liste, bislang ist das nicht geschehen. Deswegen nur eine Reaktion direkt auf den den letzten Beitrag, ohne die ganze Geschichte zu kennen:

Zitat:
1) Mit dem Main-Thread darf man niemals auf Thread-Variablen zugreifen -weder lesend noch schreibend- die jemals im Thread.Execute verwendet werden
Meinst du mit "Thread-Variablen" lokale Variablen in deiner Execute-Prozedur? Oder Felder deiner von TThread abgeleiteten Klasse?

Ich finde du machst es dir unnötig kompliziert. Es gibt zwei eiserne und einfache Regeln:
  1. Zugriff auf die VCL-Dinge darf nur vom Hauptthread aus geschehen.
  2. Wenn sich mehrere Threads für die gleichen Daten interessieren, müssen diese geschützt werden

Das war's. Zur weiteren Erläuterung:

1) Du bist standardmäßig immer im Hauptthread. Ausnahmen sind die Execute-Methode einer TThread-Klasse und beispielsweise die entsprechenden "Behandle Netzwerkpaket"-Handler von Serverkomponenten wie Indy.

2) Schützen bedeutet: Sicherstellen, dass niemand die Daten lesen (oder ändern) kann, während jemand anderes gerade am ändern ist.

Für reine Variablen bieten sich TMultiReadExclusiveWriteSynchronizer (was für ein Klassenname) perfekt an. Erstelle dir einen (oder mehrere), und bevor du eine Variable ändern willst, sage "mutex.beginRead();". Wenn du fertig gelesen hast, sag "mutex.endRead();". Ebenso mit dem Schreiben.

Parallel gibt es kritische Abschnitte, die Delphi Klasse TCriticalSection kapselt hier eigentlich schon alles was man braucht.

Ich würde vielleicht nicht einmal ein konkretes technisches Tutorial, wie man Threads in Programmiersprache XY hinbekommt, sondern etwas theoretische Grundlagen lesen. Ich habe gerade leider keinen Vorschlag parat, aber vielleicht jemand anderes.

Es tut niemandem weh, wenn du bsp. im VCL-Thread "Objekte benutzt", die in einem anderen erstellt wurden. Niemandem tut es weh, wenn mehrere Threads gleichzeitig die gleiche Methode auf einem einzigen Objekt aufrufen, und dieses Objekt in einem Thread erstellt wurde, der schon längst tot ist. Es sind eigentlich nur diese beiden Regeln.

Geändert von Der schöne Günther (20. Aug 2013 um 18:14 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.475 Beiträge
 
Delphi 12 Athens
 
#17

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

  Alt 20. Aug 2013, 18:38
Wenn ich mal auf den Call-Stack zurück kommen darf: die Fehlermeldung impliziert, daß auf die 21116. Komponente in einem Form (vermutlich dem MainForm) zugegriffen wird. Dieser Zugriff erfolgt in DispatchShortCut was allerdings in einer Schleife alle Komponenten iteriert. Sollte also nicht innerhalb dieser Schleife die Liste (aus einem anderen Thread) verändert werden, sollt dieser Fehler gar nicht vorkommen.

Bedenklich finde ich allerdings diese Zahl 21116, die ja nur über die Schleifenvariable oder einen kaputten Stack entstehen kann. Daher erst mal die Frage: Kann es sein, daß eines deiner Forms eine derart hohe Zahl von Komponenten hat? In dem Fall würde ich blind behaupten, du machst da was falsch. Deine Bemerkung "diese mittels Synchronize() an eine Komponente aus dem Hauptformular übergibt" klingt dabei aber verdächtig.

Mir ist auch aufgefallen, daß du das Execute durch unerwartet viele Synchronize-Aufrufe unterbrichst - insbesondere in der while-Schleife. Damit konterkarierst du aber eigentlich den Sinn eines Threads. Das einzige, was du mit dem Thread erreichst, ist, daß deine Anwendung beim q.Next; nicht einfriert, wenn Daten nachgeladen werden müssen. Du könntest ja mal versuchen, die benötigten Daten ohne Verwendung eines Threads mit Hilfe eines DataSetProviders in ein ClientDataSet zu laden. Wie lange dauert das?

Auch der Kommentar "damit auch beim Laden abgebrochen werden kann" hinter dem Synchronize(SyncSaveAndDelete); zeugt von einem suboptimalen Ansatz: man kann einen Thread auch (und sogar wesentlich effizienter) ohne Aufruf eines Synchronize abbrechen.

Es ist schwer in deinem Code den wirklichen Fehler zu finden, ohne daß du größere Teile des Projekts zur Verfügung stellst. Aber selbst wenn du das tust, muss sich auch noch jemand finden, der dann noch in die Fehlersuche einsteigt. Das könnte den Rahmen einer kostenlosen Hilfe unter Forenteilnehmern eventuell sprengen.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benutzerbild von Olli73
Olli73

Registriert seit: 25. Apr 2008
Ort: Neunkirchen
755 Beiträge
 
#18

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

  Alt 20. Aug 2013, 20:05
Wenn ich mal auf den Call-Stack zurück kommen darf: die Fehlermeldung impliziert, daß auf die 21116. Komponente in einem Form (vermutlich dem MainForm) zugegriffen wird. Dieser Zugriff erfolgt in DispatchShortCut was allerdings in einer Schleife alle Komponenten iteriert.
Sieht fast so aus, als ob DispatchShortCut auch über seine erstellten Speicher-Objekte/Komponenten drüber laufen würde.
  Mit Zitat antworten Zitat
berens

Registriert seit: 3. Sep 2004
434 Beiträge
 
Delphi 10.4 Sydney
 
#19

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

  Alt 20. Aug 2013, 20: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 21:40 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Olli73
Olli73

Registriert seit: 25. Apr 2008
Ort: Neunkirchen
755 Beiträge
 
#20

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

  Alt 20. Aug 2013, 20:48
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.
Das (alleine) hat hier nicht das Problem ausgelöst, sondern das was Uwe Raabe in #17 geschrieben hat. DispatchShortCut läuft alle Komponenten des Formulars durch, also auch die, denen du versehentlich im Thread frmMain als owner zugewiesen hast. -> Aber huch, wo ist denn die Komponente hin, mitten in meiner Schleife hat doch der böse böse Thread...


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!?
Bei boolean gibt es (wahrscheinlich) keine Probleme. Aber Strings z.B.(!) werden bei Bedarf umkopiert, wenn das dann mitten im Lesevorgang passiert ist das schlecht. Aber auch ohne Umkopieren, du änderst z.B. gerade im Thread den String 'abcd' auf 'wxyz', dann könnte der Hauptthrad z.B. 'wxcd', und damit rechnest du wahrscheinlich nicht.
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 3     12 3      

 

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:23 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz