Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Tmemo und threads (https://www.delphipraxis.net/189536-tmemo-und-threads.html)

p80286 22. Jun 2016 12:37

Tmemo und threads
 
Hallo zusammen,
ich habe eine Anwendung, die im Prinzip so aufgebaut ist:
Code:
PseudoCode

sqltext:=GetSQLText;
if chk(sqltext) then Querythread;


:EndofQuery
case outtype of
  file: ReadQuerytoFile(filename);
  grid: ReadQuerytoGrid(self.Grid1);
  text: ReadQuerytoMemo(self.Memo1.Lines);

:EndofReader
 curser:=cursordefault;
(Ist stark vereinfacht)

ReadQuerytoGrid ruft selber eine procedure auf, die die Daten aus der Query in ein TStrings kopiert. Das sah im Prinzip so aus:

Code:
mystrings.beginupdate;
while not query.eof do begin
  mystrings(add(query.fieldbyname(Feld).asstring);
  query.next;
end;
mystrings.endupdate;

Im Prinzip funktioniert das auch, wenn ich keine Textausgabe nutze. Dann wird die Ausgabe zu einem Glücksspiel.
Mal werden die Daten übernommen aber nicht angezeigt- sieht aus wie
Delphi-Quellcode:
TMemo.visible=false;
oder aber manchmal bekomme ich ein "ungültiges Fensterhandle" zurück.
Ebenso kann es sein, daß die Übernahme angezeigt wird und die Ergebnisse auch sichtbar sind.
Zum Testen habe ich den BeginUpdate/EndUpdate-Rahmen immer weiter vergrößert, aber das Verhalten bleibt gleich unberechenbar.
z.Zt bin ich bei:
Code:

sqltext:=GetSQLText;
if chk(sqltext) then begin
 Memo1.lines.Beginupdate;
 Querythread;


:EndofQuery
case outtype of
  file: ReadQuerytoFile(filename);
  grid: ReadQuerytoGrid(self.Grid1);
  text: ReadQuerytoMemo(self.Memo1.Lines);

:EndofReader
 Memo1.Lines.Endupdate;
 curser:=cursordefault;
Meine nächste Maßnahme wäre, Die Ergebnisse in eine "externe" Stringlist/Strings/String zu schreiben und diese dann in das Memo zu kopieren. Auf diese Weise sollten sich die beiden Threads nicht in die Quere kommen. Was mich daran stört, ist der hohe Speicherverbrauch, und daß der Zugriff auf das Stringgrid aus dem ReaderThread heraus problemlos funktioniert.

Oder suche ich u.U. am falschen Ort nach der Fehlerursache?

Ich hoffe das ist nicht zu wirr und vielen dank für's lesen.
Gruß
K-H

Sir Rufo 22. Jun 2016 13:46

AW: Tmemo und threads
 
Du fasst die Controls aber nur im Kontext des MainThreads an, richtig?

Denn wenn nicht, gibt es alles Mögliche (von tut bis kompletter Grütze :stupid:)

p80286 22. Jun 2016 14:39

AW: Tmemo und threads
 
"Anfassen" ist wohl das Stichwort. Da ich nicht weiß was der Mainthread so treibt, wenn ich es ihm nicht explizit sage, geht wohl kein Weg an dem "Zwischenpuffer" vorbei.

(Grütze ist schön und beschreibt das Verhalten recht genau!)
Edith:
ich hab's jetzt geändert:

Delphi-Quellcode:
templl:=tstringlist.Create;
self.DataReaderThread:=TDBWriteData2TextThread.Create(ADODM.ADOQuery1,templl,self.Handle,true);


procedure TForm1.CMEndRead(var msg:TMessage);
begin
  Statusbar1.Panels[0].Text:=format('_## %d %d ',[msg.WParam,msg.LParam]);
  self.M_Text.Lines.BeginUpdate;
  // Übernehme Daten
  if assigned(templl) then begin
    self.M_Text.Lines.Capacity:=templl.Count;
    self.M_Text.Lines.Text:=Templl.Text;
    FreeandNil(templl); { da xx nach xx.Free nicht NIL sein muß}
    //templl.Free;
  end;
  self.M_Text.Lines.EndUpdate;
  screen.cursor:=crdefault;
end;
Und es läuft so wie ich es gerne hätte! vielen Dank!


Gruß
K-H

himitsu 22. Jun 2016 17:35

AW: Tmemo und threads
 
M_Text.Lines.Capacity zuzuweisen bringt nichts, da das MEMO und seine API sowas nicht kennt.
Abgesehn davon, daß das Memo über die API den Text immer nur am Stück verwaltet/speichert und nicht zeilenweise.

Dieses Memo.Lines (TMemoStrings) ist nur ein Wrapper/Umleitung zur WinAPI des Memo.
Memo.Lines.Text = Memo.Text und das geht beides auf SetWindowText/WM_SETTEXT.

Messages werden immer im Erstellungsthread der Empfangskomponente verarbeitet, also kann man aus jedem Thread senden und es wird automatisch syncronisiert.
Delphi-Quellcode:
SetWindowText(M_Text.Handle, PChar(templl.Text));
// oder
SendMessage(M_Text.Handle, WM_SETTEXT, PChar(templl.Text), 0);
// nur M_Text.Handle sollte man besser im MainThread auslesen, wenn man nicht sicherstellen kann, dass M_Text.HandleAllocated=True

Wenn z.B. der Speicher nicht reicht, um den Text zu laden (=Exception), wird templl nicht freigegeben und auch das Memo zeigt nie wieder etwas an, da es weiterhin durch BeginUpdate gesperrt bleibt.

Sir Rufo 22. Jun 2016 17:41

AW: Tmemo und threads
 
Du musst nur aufpassen, dass deine Message-Queue nicht vollläuft, sonst werden diese Nachrichten von Windows einfach verworfen.

Hängt allerdings auch ab in welcher Frequenz du die Nachrichten herausknallst.

himitsu 24. Jun 2016 13:33

AW: Tmemo und threads
 
Bei SendMessage/SetWindowText wird der Queue wohl nicht voll laufen, da das ja wartet, bis die Message verarbeitet wurde.
(da bräuchte man dann schon ganz viele Threads, die das gleichzeitig versuchen)

Bei PostMessage sieht das anders aus, aber müsste das nicht einen Fehler geben, wenn der Queue voll ist und man das versucht noch rein zu quetschen?
Und leider bietet Windows ja keinen (offiziellen) Weg, um rauszufinden, wie voll der Queue ist. :freak:

sahimba 24. Jun 2016 13:40

AW: Tmemo und threads
 
Zitat:

Zitat von himitsu (Beitrag 1340990)
Bei PostMessage sieht das anders aus, aber müsste das nicht einen Fehler geben, wenn der Queue voll ist und man das versucht noch rein zu quetschen?

Gibt es ja auch. https://msdn.microsoft.com/de-de/lib...(v=vs.85).aspx
If the function fails, the return value is zero. To get extended error information, call GetLastError.
Also... wie immer. Meistens.

p80286 24. Jun 2016 14:26

AW: Tmemo und threads
 
Zitat:

Zitat von himitsu (Beitrag 1340811)
M_Text.Lines.Capacity zuzuweisen bringt nichts, da das MEMO und seine API sowas nicht kennt.

Wenn ich mich richtig erinnere hilft es aber bei anderen Tstrings-abkömmlingen und hier schadet's nicht. So hatte ich die OH verstanden.

Zitat:

Zitat von himitsu (Beitrag 1340811)
Abgesehn davon, daß das Memo über die API den Text immer nur am Stück verwaltet/speichert und nicht zeilenweise.

Das werde ich mir rahmen!

Zitat:

Zitat von himitsu (Beitrag 1340811)
Dieses Memo.Lines (TMemoStrings) ist nur ein Wrapper/Umleitung zur WinAPI des Memo.
Memo.Lines.Text = Memo.Text und das geht beides auf SetWindowText/WM_SETTEXT.

Messages werden immer im Erstellungsthread der Empfangskomponente verarbeitet, also kann man aus jedem Thread senden und es wird automatisch syncronisiert.
Delphi-Quellcode:
SetWindowText(M_Text.Handle, PChar(templl.Text));
// oder
SendMessage(M_Text.Handle, WM_SETTEXT, PChar(templl.Text), 0);
// nur M_Text.Handle sollte man besser im MainThread auslesen, wenn man nicht sicherstellen kann, dass M_Text.HandleAllocated=True

Wenn z.B. der Speicher nicht reicht, um den Text zu laden (=Exception), wird templl nicht freigegeben und auch das Memo zeigt nie wieder etwas an, da es weiterhin durch BeginUpdate gesperrt bleibt.

Davon hab ich kaum ein Wort verstanden, wohl weil ich mich noch nicht so recht mit Mesages beschäftigt habe.

Gruß
K-H

himitsu 27. Jun 2016 12:24

AW: Tmemo und threads
 
Windows-Messages werden in dem Thread ausgeführt, in welchem ein Win-Control (GDI) erstellt wurde.

Die Delphi-Controls (TForm und alles darauf) werden im Hauptthread erstellt und somit werden deren Messages auch im Hauptthread verarbeitet.
Du kannst also per SetWindowText an Edit.Handle von überall (anderer Thread oder gar andere Anwendung) etwas schicken und Windows synchronisiert das dann automatisch.

Sir Rufo 27. Jun 2016 12:31

AW: Tmemo und threads
 
IMHO wird da nichts synchronisiert, sondern der Anwendungsprozess fragt die Nachrichten-Warteschlange ab und verarbeitet die Nachrichten sofern denn welche vorliegen.

Eine Synchronisierung ist also gar nicht erforderlich :stupid:

Diese Polling-Schleife befindet sich in
Delphi-Quellcode:
TApplication.Run
Delphi-Quellcode:
procedure TApplication.Run;
begin
  ...
      repeat
        try
          HandleMessage;
        except
          HandleException(Self);
        end;
      until Terminated;
  ...
end;

p80286 27. Jun 2016 13:19

AW: Tmemo und threads
 
Zitat:

Zitat von himitsu (Beitrag 1341234)
Du kannst also per SetWindowText an Edit.Handle von überall (anderer Thread oder gar andere Anwendung) etwas schicken und Windows synchronisiert das dann automatisch.

Ich hab mal versucht mich schlau zu machen, SetWindowText scheint aber nur innerhalb eines Threads zu funktionieren, das Mittel der Wahl wäre dann SendMessage(..WM_SETTEXT..).
(aber wo bleibt dann BeginUpdate/EndUpdate ?)


Gruß
K-H

himitsu 27. Jun 2016 13:25

AW: Tmemo und threads
 
Unnötig?
Der Text wird eh nur mit dem einem Befehl reingegeben und es gibt somit nur eine Aktualisierung.

OK, "synchronisiert" wird es nur "indirekt".
Windows fügt diese Message synchronisert in die MessageQueue des Erstellerthreads des Controls ein.
Und da nur der eine Thread seine Messages ausliest und verarbeitet, werden dort alle Messages auch immer nur nacheinander abgerifen/verarbeitet.

p80286 28. Jun 2016 15:21

AW: Tmemo und threads
 
Ich hab es mal umgesetzt, und es fluppt:
Delphi-Quellcode:
procedure TDirThread.Execute;
var
  ll : tstrings;
begin
  ll:=tstringlist.Create;
  Dirliste(Muster,tstrings(ll)); //liefert alle subdirs unterhalb von muster
  SendMessage(FMsgCompHandle, WM_SETTEXT, 0, Longint(PChar(ll.Text))); //FMsgCompHandle ist das Handle vom Memo1
  ll.Free;
end;
Vielen dank
K-H

himitsu 28. Jun 2016 17:55

AW: Tmemo und threads
 
Unter Win64 wird es mit LongInt natürlich knallen.
Selbst Integer/Cardinal ist inzwischen falsch, da dessen Größe eingefroren wurde.

Als Casts für Pointer solltest du immer nur die Typen IntPtr (falls das definiert ist), NativeInt NativeUInt (das neue "Integer") oder besser noch LPARAM, WPARAM und LRESULT verwenden.
Letztere sind die "eigentlichen" Typen der Parameter, für z.B. SendMessage, weswegen sie hier natürlich immer die richtigen Ergebnisse liefern.

NativeUInt/Cardinal statt NativeInt/Integer, da Pointer natürlich vorzeichenlos sind. :zwinker:

p80286 29. Jun 2016 10:54

AW: Tmemo und threads
 
Danke für den Hinweis, das kommt davon wenn man blöde abschreibt.
Delphi-Quellcode:
SendMessage(FMsgCompHandle, WM_SETTEXT, 0, LPARAM(PChar(ll.Text)));
(für mich ist Pchar noch in Ordnung!)

Aaber...

BS ist ein 64Bit-Windows. Kompiliert wurde das Programm mit D7 (also 32 Bit).
Hier verweist SendMessage auf SendMessageA (Ansi-Text?) und es gibt keine Unterscheidung zwischen 32 und 64 Bit. Warum sollte es auch knallen? Ich übergebe einen Wert (der unterhalb der 32Bit Grenze liegt) an eine 64Bit Speicherstelle. Das sollte passen. Etwas anders ist es wenn ich in einem 64Bit Kompilat einen 64Bit-Wert durch einen 32-Bit Typen "verhunze". Das kann gut gehen, aber man soll sich nicht wundern wenn es nicht gut geht.

Gruß
K-H


Alle Zeitangaben in WEZ +1. Es ist jetzt 11:02 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