![]() |
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
So nochmal ich.
Der Code lässt sich nun schon mal übersetzen. Danke Dirk. Funktioniert nur noch nicht so wie gedacht ... :) Beim ersten Eintrag geht alles glatt. Das Event wird gesetzt, der Thread rennt an, er verarbeitet die Daten und macht das Dispose. Wenn ich einen zweiten Eintrag hinzufüge macht der Thread aber nüscht mehr auch wenn das event abgefeuert wird. Laufe tut der Thread allerdings wohl noch - sieht man ja im Debugger. Zum Erzeugen der Klasse und des Thread nutze ich den Code:
Delphi-Quellcode:
var Form3 : TForm3;
Kette : TDataClass; Work : TWorker; implementation procedure TForm3.Button1Click(Sender: TObject); begin Kette.Add('Name', 1, 2, 3, 4); end; procedure TForm3.FormCreate(Sender: TObject); begin Kette := TDataClass.Create; work := TWorker.Create(Kette); work.Start; end; |
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
OK, dann ist der fortlaufende Index für den Zugriff garnichtmal nötig.
Hier gibt es aber erstmal ein Problem: Wenn jetzt zwei Threads immer alle Daten verarbeiten sollen und gleichzeigtig einer der Threads die Liste löschen würde. Lösungen: - entweder für jeden der Threads eine eigene Liste - oder Beides in einem Thread lösen (also die beiden Dateien zusammen erstellen) Die Abfrage der aktuellen Werte, für das Livemonitorin würde ich dann auch nicht über diese Liste machen, denn was willst du Anzeigen, wenn gerade in diesem Moment diese Liste noch/wieder leer ist? |
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Liste der Anhänge anzeigen (Anzahl: 1)
Moin !
Zitat:
Zitat:
Ich denke der Code kommt noch ein bisserl durcheinander mit FFirst und FLast. Denn kann ja nicht sein das FLast noch einen Pointer beinhaltet obwohl der einzige dateneintrag gerade verarbeitet wurde: |
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Wenn du die Live-Werte extra vorhälst, dann wird FLast nicht benötigt und eine stinknormale einfach verkettete Liste reicht vollkommen aus.
ach nee, für's Anhängen neuer Daten wird FLast ja dennoch benötigt.
Delphi-Quellcode:
Da hier gleich die ganze Liste aus der Datenhaltung rausgeholt wird, ist diese bereit sofort neue Daten aufzunehmen und wird weniger bei Auslesen blockiert.
uses
Windows, Classes, SysUtils, SyncObjs; type POneData = ^TOneData; TOneData = record Text: string; Value1: Double; Value2: Double; Value3: Double; Value4: Double; Next: POneData; end; TDataClass = class private FEvent: TEvent; FLock: TCriticalSection; FFirst, FLast: POneData; FLive: TOneData; public constructor Create; destructor Destroy; override; procedure Add(const Name: string; const V1, V2, V3, V4: Double); function ExtractAll: POneData; function GetLive: TOneData; function WaitAndCheckForNewData(TimeOut: Cardinal): Boolean; end; TWorker = class(TThread) private FDataClass: TDataClass; protected procedure Execute; override; public constructor Create(DataClass: TDataClass); end; constructor TDataClass.Create; begin inherited Create; FEvent := TEvent.Create(nil, True, False, ''); FLock := TCriticalSection.Create; end; destructor TDataClass.Destroy; var tmp: POneData; begin while FFirst <> nil do begin tmp := FFirst; FFirst := FFirst.Next; Dispose(tmp); end; FEvent.Free; FLock.Free; inherited Destroy; end; procedure TDataClass.Add(const Name: string; const V1, V2, V3, V4: Double); var NewData: POneData; begin New(NewData); NewData.Text := Name; NewData.Value1 := V1; NewData.Value2 := V2; NewData.Value3 := V3; NewData.Value4 := V4; NewData.Next := nil; try FLock.Acquire; try FLive := NewData^; if Assigned(FLast) then begin FLast.Next := NewData; FLast := NewData; end else begin FFirst := NewData; FLast := FFirst; end; FEvent.SetEvent; finally FLock.Release; end; except Dispose(NewData); end; end; function TDataClass.ExtractAll: POneData; begin FLock.Acquire; try FEvent.ResetEvent; Result := FFirst; FFirst := nil; FLast := nil; finally FLock.Release; end; end; function TDataClass.GetLive: TOneData; begin FLock.Acquire; try Result := FLive; finally FLock.Release; end; end; function TDataClass.WaitAndCheckForNewData(TimeOut: Cardinal): Boolean; begin Result := FEvent.WaitFor(TimeOut) = wrSignaled; end; procedure TWorker.Execute; var PData, PData2: POneData; begin repeat if FDataClass.WaitAndCheckForNewData(100) then begin PData := FDataClass.ExtractAll; try while Assigned(PData) do begin PData2 := PData; PData := PData.Next; try Verarbeite(PData2^); finally Dispose(PData2); end; end; except // falls Exception, restlichen Speicher aufräumen und Werte verwerfen // aber es wäre auch möglich die Daten wieder in die Liste einzufügen // oder man "ignoriert" Exceptions und setzt einfach die obere Schleife fort while Assigned(PData) do begin PData2 := PData; PData := PData.Next; Dispose(PData2); end; Raise; end; end; until Terminated; end; constructor TWorker.Create(DataClass: TDataClass); begin inherited Create(True); FDataClass := DataClass; Resume; end; Bei Verarbeite werden die Daten dann einfach in beide Dateien (Rohdaten und umgerechneten CSV-Daten geschrieben). Die aktuellsten Live-Werte bekommt man über DataClass.GetLive geliefert. |
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Moin !
@himitsu: Vielen Dank für deine Erweiterung. Das klappt nun erstaunlich gut :thumb: Auch die Speicherbelastung bleibt wie erwartet niedrig. :) Hätte dann noch ein paar wetere Fragen (und hoffentlich gehe ich euch nicht zu sehr auch den Sac* :roll: ). 1) Event an Thread Wenn ich nun möchte das der Thread nur jede Sekunde die Daten verarbeitet, dann könnte ich doch einfach in TDataClass.Add bei FEvent.SetEvent eine Zeitliche Überprüfung einbauen. Also sowas wie Millisecondsbetween(Now, Then) >= 1000 ... ? 2) Ich möchte ja die Daten auch in ein Chart schreiben. Und am liebsten würde ich die Daten natürlich so im Chart ablegen, dass der Hauptthread der Anwendung möglichst unbelastet bleibt.
Delphi-Quellcode:
So funktioniert das zwar im Thread, aber eigentlich müsste es ja über ein Synchronize laufen, oder? Aber das läuft ja dann wieder im Hauptthread. :gruebel:
TWorker = class(TThread)
private FDataClass : TDataClass; FChart : TChart; protected procedure Execute; override; public constructor Create(DataClass: TDataClass; Chart : TChart); end; 3) Wie verhält sich TDataClass wenn der Hauptthread ausgelastet ist? Derzeit erzeuge ich die Instanz von TDataClass ja im Hauptthread der Anwendung. Wenn die aber nun gerade zu 100% ausgelastet ist, was macht dann TDataClass? Dann würde doch ein Add nicht mehr zur Anwendung kommen, oder? Das wäre natürlich fatal weil ich dann mitunter Daten verlieren könnte. Könnte man TDataClass ggf. selber als Thread auslegen und somit vom HAuptthread abkapseln? |
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Die Kommunikation mit der Peripherie sollte in einem eigenen Thread laufen welcher dann Add() aufruft. Immer wenn eine Funktion aufgerufen wird dann findet das immer im Kontext des aufrufenden Threads statt, also in einem anderen Thread als das Execute() läuft und unabhängig davon welcher Thread das Objekt erstellt hat. Solange der Kommunikationsthread (für den ich eine erhöhte Priorität empfehlen würde) zum Zuge kommt werden auch die Daten angenommen.
Da ich nicht weis wie schnell/gleichmäßig die Daten reinkommen ist es schwer zu sagen ob es sinnvoller ist eine Sekunde zu sammeln (GetTickCount o.ä.) oder von vornherein jedes Paket mit einem Zeitstempel zu versehen. Der Kommunikations-Thread könnte den Event "Daten vorhanden" z.B. nach jeweils 100 Paketen oder 1000ms setzen und der Worker übernimmt dann den ganzen Datensatz aus diesem Zeitraum (Das wäre dann vielleicht wieder die Variante mit dem Austauschen der ganzen Liste). Mit TChart habe ich noch nicht gearbeitet, könnte mir aber prinzipiell vorstellen inn einem eigenen Thread ein Bitmap aufzubauen welches dann in einem kurzen Synchronize ausgegeben wird. Dieses Synchonize sollte auf keinen Fall aus dem Kommunikationsthread kommen damit dieser unabhängig von der Oberfläche arbeiten kann. Grüsse, Dirk |
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Moin !
Zitat:
Sagen wir mal es gibt einen Kommunikationsthread und die Instanz der Datenklasse liegt im MainForm und damit ja eigentlich auch im MainThread. Wenn ich nun das Add aus dem Kommu-Thread raus aufrufe, läuft das dann wirklich im Kommu-Thread ? Zitat:
@himitsu: Hast du meine PN gelesen? |
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Die Instanz liegt im Speicher, nicht im Threadkontext. Und alle deine Threads haben Zugriff auf den Speicher, man muss halt nur schauen das die Zugriffe geortnet ablaufen. Das wird über Synchronisierungsobjekte wie TCriticalSection oder das in TTread implementierte Synchronize durchgeführt. Synchronize legt prinzipiell die Adresse der Prozedur welche der Mainthread aufrufen soll in einer Liste ab und schickt eine Nachricht (WM_NULL) an den Mainthread das in der Liste Arbeit wartet. Bis der Mainthread das getan hat schläft der Thread, läuft also danach erst weiter. Deswegen ist Synchronize so eine Bremse welche man wirklich nur "wenn nötig" benutzen sollte. Verriegelungen die unabhängig vom Mainthread stattfinden können/dürfen sind also besser über die SyncObjekte zu realisieren.
TChart: Dann also die ValueLists für´s TChart aufbereiten und in einem Synchronize übergeben + zeichnen. Ist doch auch aus dem Thread kein Problem. Grüsse, Dirk |
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Liste der Anhänge anzeigen (Anzahl: 1)
Moin !
So ich habe jetzt mal alles in ein Projekt gegossen. Erstellt ist es mit D2010, aber ich denke es sollte auch mit anderen Delphi Versionen nutzebar sein. Man kann mit dem Button "Add Input" an die verkettete Liste Daten anhängen. Das geht recht flott und der Output Thread arbeitet die Liste dann wieder ab. Parallel gibt es einen Timer der jede Sekunde DatenKette.GetLive.Value1 ausgibt. Ok, nun kommt aber der Spannende Teil ... Der TOutputThread ackert nun durch die Liste und jetzt müssen die Daten irgendwie mal in das Chart. Ich habe zu dem Zweck schon mal ein TChart Standard (ist bei jedem Delphi dabei) auf das Formular gepackt. Ich habe dann testweise mal dem OutputThread das Chart übergeben:
Delphi-Quellcode:
Und dann im execute folgendes:
constructor Create(DataClass: TDataClass); Chart : TChart);
Delphi-Quellcode:
Das funktioniert, aber ich würde mal vermuten das es der falsche Weg ist, denn das wäre ja ein direkter Zugriff vom Thread auf das Chart :gruebel:
fChart[0].Add(PData2.Value1);
fChart[1].Add(PData2.Value2); fChart[2].Add(PData2.Value3); fChart[3].Add(PData2.Value4); Zitat:
|
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Zitat:
Wer mit der VCL arbeitet muss damit leben, nur der Hauptthread darf auf Steuerelemente zugreifen. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 04:46 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-2025 by Thomas Breitkreuz