![]() |
Gelegentliche Hänger
Moin!
Der Titel ist etwas allgemein, aber mein Problem ist etwas umfassender in der Beschreibung und schwer in 3-4 Worte zu fassen. Folgende Basis: Ich habe eine Formularklasse von TCustomForm abgeleitet. Diese besitzt einen Thread, der von aussen mit Aktualisierungsaufträgen gefüttert wird, wodurch Komponenten auf dem Formular mit Daten aus einer Datenbank befüllt werden. Hintergrund ist ein Client-Server-System zur Anlagenvisualisierung in der Industrie, bei der ein Server Daten aus einer SPS pollt, diese in die DB schreibt, und angemeldete Clients via TCP über Neuigkeiten benachrichtigt. Die genannten Formulare reagieren auf eben diese Benachrichtigungen. (Diese enthalten je einen Tabellennamen und eine Liste der Satz-IDs in denen Änderungen waren.) Die Struktur in (Teil-)(Pseudo-)Codefragmenten:
Delphi-Quellcode:
Dass mir gelegentlich das Programm komplett stehen bleibt, scheint gerade gelöst zu sein: Der Server nutzt einen gemeinsamen TServerSocket, über den mehrere Threads Nachrichten verschicken (ein Thread pro SPS-Verbindung), und obwohl ich die Notify-Methoden gegeneinander verriegel (der Server läuft auch klaglos), scheint das beim Empfang auf Clientseite Probleme zu machen. Hat der Server nur einen Thread, scheint das zu gehen.
unit MyForm;
const WM_VCLUPDATE = WM_USER + 943; type TBytes = packed array of Byte; TCommBuffer = class public buf: TBytes; end; TVCLUpdateData = class public Instance: TObject; ClassType: TClass; PropName: String; Value: Variant; end; TUpdateThread = class(TThread) private procedure MakeUpdatesTabelle1; procedure MakeUpdatesTabelle2; protected procedure Execute; override; public RawBuffers: TObjectList; end; TMyForm = class(TCustomForm) private UpdateThread: TUpdateThread; procedure HandleUpdates(var Msg: TMessage); message WM_VCLUPDATE; protected procedure DoShow; override; public RelevanteKomponenten: TObjectList; UpdateItems: TObjectList; UpdateCS: TCriticalSection; AddCS: TCriticalSection; constructor Create; // Füllt "RelevanteKomponenten" anhand von Datenbankeinträgen mit den Instanzen auf dem Formular procedure AddBuffer(buf: TBytes); end; implementation // Wird vom Hauptformular im Ereignishandler OnSocketRead() aufgerufen (ein TClientSocket) procedure TMyForm.AddBuffer(buf: TBytes); begin if self.Visible then begin AddCS.Enter; try UpdateThread.RawBuffers.Add(TCommBuffer.Create(copy(buf, 0, Length(buf))); finally AddCS.Leave; end; end; end; procedure TMyForm.HandleUpdates(var Msg: TMessage); var item: TVCLUpdateItem; begin UpdateCS.Enter; try while UpdateItems.Count>0 do begin item := TCVUpdateItem(UpdateItems[0]); try if Assigned(item.Instance) and (item.Instance is item.ClassType) and ((item.Instance as TControl).Parent.Visible) then SetPropValue((item.Instance as item.ClassType), item.PropName, item.Value); finally UpdateItems.Delete(0); end; end; finally UpdateCS.Leave; end; end; procedure TMyForm.DoShow; begin inherited DoShow; SetzeAlleUpdateFlagsImThread; // Damit nach Sichtbarmachen ein komplettes Update aus der DB geschlabbert wird end; procedure TUpdateThread.Execute; begin repeat if RawBuffers.Count > 0 then InterpretiereBufferInhalteUndSetzeFlagsFürAusstehendeUpdates; // Umrahmt von "AddCS.Enter; -- AddCS.Leave;" if UpdateFlagFürTabelle1 and Tabelle1Relevant then MakeUpdatesTabelle1; if UpdateFlagFürTabelle2 and Tabelle2Relevant then MakeUpdatesTabelle2; // usw. Sleep(1); until Terminated; end; procedure TUpdateThread.MakeUpdatesTabelle1; var obj: TObject; begin SelectDatenFromSQL; FForm.UpdateCS.Enter; try while not Query.Eof do begin obj := FForm.FindeKomponenteAusInDessenCreateErstellterListeRelevanterKomponenten('NameAusTabelle'); FForm.UpdateItems.Add(TVCLUpdateData.Create(obj, obj.Class, 'PropertyAusTabelle', WertAusTabelle)); Query.Next; end; finally FForm.UpdateCS.Leave; end; PostMessage(FForm.Handle, WM_VCLUPDATE, 0, 0); end; Ein anderes Problem bleibt aber! Ab und zu, ganz selten und schwer reproduzierbar hört ein beliebiges Formular auf Basis der o.g. TMyForm-Klasse auf sich upzudaten. Logging brachte jedoch zutagen, dass der UpdateThread weiterhin läuft, und auch der Messagehandler reagiert - es wird einfach nur nicht mehr Neugezeichnet, und die Controls reagieren auch nicht mehr auf Clicks und Tastatur. Jetzt war ich schon SO vorsichtig was das Fummeln an VCL Komponenten in Kombination mit Threads angeht, und dennoch hängt es gelegentlich. Allerdings auch gerne mal erst nach 2-5 Tagen, in denen das Programm durchlief und tausende Updatezyklen problemlos durchlaufen hat. Ab und an - ebenfalls selten, aber sehr störend: Wenn ich zwischen zwei solcher Formulare hin und her schalte (das alte .Hide, das neue .Show) bleibt ab und an das gesamte Clientprogramm stehen. Noch bevor das Komplett-Update im DoShow überhaupt angestoßen ist. :? Wo kann ich hier noch auf einen Hammer laufen? Ich debugge mir hiermit schon 2 Tage einen Wolf, und seh vermutlich nicht mehr alles so klar. |
AW: Gelegentliche Hänger
Wenn ich die ganzen unsynchronisierten Zugriffe auf Komponenten des MainThreads sehe wundere ich mich dass das ganze überhaupt funktioniert....
|
AW: Gelegentliche Hänger
Wo? Genau das wollte ich doch mittels der Entkopplung durch die Message WM_VCLUPDATE verhindern, dessen Handler doch im Hauptthread-Kontext läuft - oder nicht?
|
AW: Gelegentliche Hänger
Du greifst über FForm. so wie ich es verstehe in den Hauptthread, auf welcher Ebene Query und zugehörige Connection hängen kann ich nicht erkennen...
|
AW: Gelegentliche Hänger
Über FForm greife ich nur auf die CriticalSections sowie auf Listen zu, deren Zugriff über eben diese CriticalSections abgesichert wird. Da wird niemals eine VCL Komponente drüber angefasst, abgesehen von ihrern Referenzen, die in die UpdateItems-Liste gehören. Die Referenzen stammen aber aus einer TObjectList (nicht etwa mit FindComponent), und weiter als den blanken Pointer (und ClassType) geht der Thread nicht. Die Referenzen sollten ja über den gesamten Lebenszeitraum des Formulares konstant bleiben hoffe ich. Es wird zumindest nichts dynamisch erzeugt/zerstört.
Query und Connection liegen im Kontext von TUpdateThread, und werden in dessen Konstruktor erzeugt. (UniDAC). Ein ähnliches, nur "dümmeres" Verfahren läuft bei einigen Kunden auch schon Störungsfrei seit Monaten. Dort schickt der Server nur keine Updatebenachrichtigungen, sondern der UpdateThread macht ganz naiv jede Sekunde von selber einen vollen Rausch über die Datenbank und updated. Ich wollte das durch die Notifies jetzt nur verschlanken, da das Verfahren sonst in größeren Projekten doch etwas arg auf die Netz- und Rechnerperformance geht. Meinen Loggingbemühungen nach scheint der wesentliche Knackpunkt das Eintreffen von TCP-Paketen zu sein. Wenn die so schnell kommen, läuft was schief. Wie ist das eigentlich: Wenn ich mit TServerSocket.SendBuf() etwas versende, was auf jeden Fall kleiner ist als ein TCP Payload - bekomme ich dann wirklich im Client ein OnSocketRead pro Server.SendBuf? Oder kann mir es passieren, dass auch mal 2 oder mehr SendBufs an einem Stück im Client eintreffen? Oder gar Fragmente trotz der geringen Größe der Nachrichten? Nicht, dass ich hier am Ende an der komplett falschen Stelle suche! |
AW: Gelegentliche Hänger
Zitat:
Daher würde ich versuchen, die (deprecated) ServerSocket und ClientSocket Komponenten auszutauschen, Synapse zum Beispiel ist einfach zu verwenden, solide und performant. Auch ist der Code, den man für blockierende Socketoperationen schreibt, einfacher zu lesen da er sich mehr am Protokoll orientieren kann. In Tests erreiche ich kontinuierlich über 8.000 Nachrichten pro Sekunde zu/von einem in Java geschriebenen Server, der gleichzeitig mit dem Delphi Programm läuft (auf einem Vista-Billiglaptop). |
AW: Gelegentliche Hänger
Ich könnt mir in die Furt beißen. Eine einfache Stop-Sequenz im Protokoll mit einem kleinen Empfangspuffer, und aller Spuk ist vorbei. Mit Indy werde ich irgendwie noch nicht so recht warm. Aber dieses einfache Frame um meine Notify-Päckchen wirkte wunder. Mein Thread-Konzept scheint also doch sehr stabil, und die ganze Zeit waren falsch interpretierte Bröckchen vom Server das Problem. Argh :stupid:
(Nicht, dass mir das grundlegende Problem diesbezüglich unbekannt gewesen wäre, aber aus Faulheit falsche Annahmen treffen geht doch öfter nach hinten los als gewünscht.) Ich glaube das Thema ist hiermit durch. Danke euch! |
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:01 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz