![]() |
Liste (ähnlich dyn Array) mit fortlaufendem Index?
Moin !
Gibts eigentlich eine Liste mit einer Art fortlaufendem Index? Bsp: In SQL gibts ja den Feldtyp AutoInc. Bedeutet das Feld bekommt immer einen Wert MAX+1 und das unabhängig davon ob ich vorne in der Liste etwas lösche oder nicht. Der Index is immer eindeutig und ich kann damit einen Datensatz eben auch eindeutig identifizieren (u.a.). Gibt es sowas auch in Delphi als fertiges Objekt? Kann das evtl. TQueue oder TCollection? Hintergrund: Ich möchte eine Liste aufbauen die ich dynamisch ergänze. Aus dieser Liste wiederum möchte ich Indexbezogen Daten asynchron auslesen und verarbeiten und dann die Daten aus der Liste löschen. Wenn ich jetzt z.B. ein dyn. Array dafür verwende, dann habe ich keinen eindeutigen Index auf den ich zugreifen kann, denn durch das löschen verschiebt sich alles immer wieder. Das Löschen wiederum ist wichtig weil ich die Daten nicht unnötig im Speicher halten möchte. :gruebel: |
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Halte den "Index" als Teil der Daten
|
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Bei TCollection haben die Items eine eindeutige ID property, die beim Einfügen in die Collection gesetzt wird. Der Index kann sich ändern, die ID nicht.
|
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
So etwas habe ich ehrlich gesagt noch nie gebraucht, schließlich stellt doch der Pointer auf das Objekt selbst schon eine eindeutige ID dar :?: Einziger Unterschied ist, dass die Objekt-Adressen natürlich nicht unbedingt aufsteigend sind.
|
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Zitat:
der Index für den Zugriff auf die Daten wäre dann nicht der "Index" der Daten innerhalb der Datenhaltung, sondern der Wert eines Feldes innerhalb der einzelnen Daten. Beim Zugriff müßten dann nur die Daten mit dem entsprechende Indexwert gesucht werden ... ähnlich .Name bei einer Stringliste. |
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Warum sollte das Objekt selbst einen festen Index haben?
Wird das Objekt irgendwo referenziert, kann ich dort direkt auf die Felder zugreifen. Die Position in einer Liste mit IndexOf(Object). Falls ich wirklich so etwas wie eine ID brauch, dann die Adresse im Speicher Cardinal(Pointer(Object)). Allerings kann ein neues Objekt dann eventuell die Speicheradresse eines bereits freigegebenen Objektes bekommen. |
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Moin !
Erstmal danke für die Antworten bis hierher. Zitat:
Ich möchte eben an der Liste Daten anfügen z.B. mit einem Thread. Und parallel dazu möchte ich Teile der Daten auswerten lassen. Dazu wäre es eben sehr praktisch einen fortlaufenden festen Index zu haben. Denn so könnte ich der auswertenden Routine sagen "Bearbeite mal Index 34-103" ohne mir jemals Gedanken machen zu müssen ob diese Index auch wirklich nach dem Start der Bearbeitung weiterhin so existieren. Denn wie gesagt möchte ich die Bereiche der Liste die bearbeitet worden sind löschen. Zitat:
|
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Ich habe das Problem mal wie folgt gelöst:
Beide Threads ("Sammler" und "Arbeiter") haben je eine TList. Der Sammler füllt fleissig seine Liste und setzt einen Event über den der Arbeiter erkennt das neue Daten vorliegen (WaitForSingleObject). Dann tauscht er seine abgearbeitete, leere Liste per
Delphi-Quellcode:
mit dem Sammler aus.
FWorkList := TList(InterlockedExchange( Integer(FSammler.FList), Integer(FWorkList)));
Der Sammler macht immer ein einfaches Add() und der Arbeiter kann von 0 bis Pred(Count) zugreifen ohne das sich die beiden jemals ins Gehege kommen. Der eindeutige Index ist dann überflüssig. MfG, Dirk |
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Moin Tryer,
das klappt bei mir leider nicht so ganz. Denn bei mir müssen zwei "worker" aus der Liste lesen - ggf. sogar mehr. Ich habe es jetzt mal mit TList versucht. Meine Hoffnung war das ich dort Pointer einhängen kann. Das klappt auch. Danach habe ich die Einträge der Liste auf NIL gesetzt. Aber es wird kein Speicher freigegeben. Das passiert vermutlich nur bei Delete. Hmm scheint so als müsse ich doch einen eigenen Index mitführen. Das passt mir zwar irgendwie gar nicht weil ich dann suchen muss, aber mal sehen ... |
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Zitat:
Und das Suchen würde natürlich in das "Array"-Objekt integriert. Halt so ähnlich, als wie es bei StringListe.Name[] gelöst ist. |
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Moin !
Zitat:
Zitat:
|
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Zitat:
Grüsse, Dirk |
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Moin Dirk,
Anbei mal mein Code. Wie würde das ansatzweise aussehen wenn ich verkettete Listen verwende?
Delphi-Quellcode:
unit DataClass;
interface uses Classes, SyncObjs, SysUtils, Contnrs; type TOneData = record ID : Int64; Zeichenkette : string[255]; Value1, Value2, Value3, Value4 : Double; Kill : Boolean; end; TDataClass = class(TPersistent) private { Private-Deklarationen } fDataMutex : TMutex; // nur ein Thread darf Daten schreiben / löschen fNotify : TNotifyEvent; fData : Array of TOneData; fID : Int64; // eindeutiger Index public { Public-Deklarationen } constructor Create; Destructor Kill; Procedure Add(Name : String) ; overload; Procedure Add(V1, V2, V3, V4 : Double); overload; procedure DeleteItem(ID : Int64); Function GetLength : Integer; procedure GetIDs(var firstID, lastID : Int64); Function Read(Item : Integer) : String; overload; procedure Read(var V1, V2, V3, V4 : Double; Item : Integer); overload; function Done(Item : Integer) : Boolean; published property OnNotify: TNotifyEvent read fNotify write fNotify; end; implementation constructor TDataClass.Create; begin inherited; fDataMutex := TMutex.Create(nil, False, 'DataMutex', True); fID := 0; end; Procedure TDataClass.Add(Name : String); begin fDataMutex.Acquire; Inc(fID); SetLength(fdata, Length(fdata) + 1); fdata[Length(fData) - 1].Zeichenkette := DateTimeToStr(Now) + ' ' + Name; fdata[Length(fData) - 1].ID := fID; fDataMutex.Release; // Notify auslösen ... if Assigned(fNotify) then fNotify(Self); end; Procedure TDataClass.Add(V1, V2, V3, V4 : Double); begin fDataMutex.Acquire; Inc(fID); SetLength(fdata, Length(fdata) + 1); fdata[Length(fData) - 1].Value1 := V1; fdata[Length(fData) - 1].Value2 := V2; fdata[Length(fData) - 1].Value3 := V3; fdata[Length(fData) - 1].Value4 := V4; fdata[Length(fData) - 1].ID := fID; fDataMutex.Release; end; procedure TDataClass.DeleteItem(ID : Int64); begin // fdata end; Function TDataClass.GetLength : Integer; begin fDataMutex.Acquire; Result := Length(fData); fDataMutex.Release; end; procedure TDataClass.GetIDs(var firstID, lastID : Int64); var I : Integer; begin fDataMutex.Acquire; for I := 0 to Length(fData) - 1 do begin if fData[I].Kill = False then Break; end; firstID := I; fDataMutex.Release; end; Function TDataClass.Read(Item : Integer) : String; begin fDataMutex.Acquire; Result := fData[Item].Zeichenkette; fDataMutex.Release; end; procedure TDataClass.Read(var V1, V2, V3, V4 : Double; Item : Integer); begin fDataMutex.Acquire; V1 := fData[Item].Value1; V2 := fData[Item].Value2; V3 := fData[Item].Value3; V4 := fData[Item].Value4; fDataMutex.Release; end; function TDataClass.Done(Item : Integer) : Boolean; begin fDataMutex.Acquire; Result := fData[Item].Kill; fDataMutex.Release; end; Destructor TDataClass.Kill; begin inherited; fDataMutex.Free; end; end. |
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Speicherst Du entweder die Zeichenkette oder die Realwerte? Die Vermischung finde ich unübersichtlich, aber diesbezügich kannst Du das ganze ja selber anpassen.
"Mal eben", also garantiert nicht fehlerfrei:
Delphi-Quellcode:
Grüsse,
type
POneData = ^TOneData; TOneData = record Zeichenkette: string; Value1: Double; Value2: Double; Value3: Double; Value4: Double; _Next: POneData; end; TDataClass = class private FEvent: THandle; FLock: TRTLCriticalSection; FFirst: POneData; FLast: POneData; public constructor Create; destructor Destroy; override; procedure Add(Name: string; V1, V2, V3, V4 : Double); procedure ExtractFirst: POneData; procedure Lock; procedure Unlock; property NewDataEvent: TEvent read FEvent write FEvent; end; TWorker = class(TThread) private FDataClass: TDataClass; protected procedure Execute; override; public constructor Create(DataClass: TDataClass); end; implementation constructor TDataClass.Create; begin inherited Create; FFirst := nil; FLast := nil; FEvent := CreateEvent(nil,True,False,nil); InitializeCriticalSection(FLock); end; Procedure TDataClass.Add(Name : string; V1, V2, V3, V4 : Double); var NewData: POneData; begin New(NewData); NewData^._Next := nil; NewData^.Zeichenkette:= Name; NewData^.Value1 := V1; NewData^.Value2 := V2; NewData^.Value3 := V3; NewData^.Value4 := V4; try Lock; try if Assigned(FLast) then begin FLast^._Next := NewData; FLast := NewData; end else begin FFirst := NewData; FLast := FFirst; end; SetEvent(FEvent); finally Unlock; end; except Dispose(NewData); end; end; procedure TDataClass.Lock; begin EnterCriticalSection(FLock); end; procedure TDataClass.Unlock; begin LeaveCriticalSection(FLock); end; function TDataClass.ExtractFirst: POneData; begin Result := nil; Lock; try ResetEvent(FEvent); if Assigned(FFirst) then begin Result := FFirst; FFirst := FFirst^._Next; end; finally Unlock; end; end; destructor TDataClass.Destroy; var tmp: POneData; begin while FFirst <> nil do begin tmp := FFirst; FFirst := FFirst^._Next; Dispose(tmp); end; CloseHandle(FEvent); DeleteCriticalSection(FLock); inherited Destroy; end; procedure TWorker.Execute; var PData: POneData; begin repeat if WaitForSingleObject(FDataClass.NewDataEvent, 100) = WAIT_OBJECT_0 then begin PData := FDataClass.ExtractFirst; if Assigned(PData) then begin try Verarbeite(PData^); finally Dispose(PData); end; end; end; until Terminated; end; constructor TWorker.Create(DataClass: TDataClass); begin inherited Create(True); FDataClass := DataClass; Resume; end; Dirk EDIT: kleine Korrektur: Das SetEvent im Add() muss in den abgesicherten Bereich. |
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Moin !
der Code bringt noch ne Menge Fehler. So kann er mit dem TEvent so nichts anfangen. Das kommt immer [DCC Fehler] Thread.pas(53): E2010 Inkompatible Typen: 'TEvent' und 'Cardinal' oder umgekehrt Das Thread Create ist wohl auch nicht ganz sauber, aber das bekomme ich schon hin. Könntest du mir mal ganz kurz erklären welche Rolle TEvent generell spielt? Sehe ich das richtig, dass das eine Art Notifikation an den Thread ist, das neue Daten vorhanden sind? Und wie würde ich bei dem Konstrukt einen Worker aufsetzen der jede Sekunde nur jeweils den letzten eintrag der Kette liest? Danke auf jeden Fall schon mal bis hierhin ! |
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Sorry, muss natürlich vom Typ THandle (bzw. Cardinal) sein. Und natürlich heißt es "constructor Create(..)" in der Implementierung. Schreibfehler halt ;)
Mit dem Event(siehe Hilfe zu CreateEvent) werden die gerade arbeitslosen, wartenden Worker aktiviert um sich die Daten abzuholen. Das ganze ist natürlich so ausgelegt das der Worker sich einfach den Job holt der am längsten in der DataClass steht (halt "First"), und dass sobald er den letzten Job abgearbeitet hat. Den Worker könnte man natürlich über ein Sleep(1000) o.ä. pausieren lassen, aber darin kann ich den Sinn nicht erkennen. Was soll denn da überhaupt und zu welchem Zweck verarbeitet werden? Grüsse, Dirk |
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Zitat:
Ich würde TEvent aber weglassen oder zumindestens Folgendes in eine Schleife legen, welche solange arbeitet, bis keine Daten mehr in der Liste stehen oder bis Terminated = True.
Delphi-Quellcode:
Denn so würde die Liste nie vollständig abgearbeitet, wenn mal mehrere Einträge gleichzeitig in der Liste stehen, da immer nur ein Eintrag je "Signal" verarbeitet wird, da das Signal nach dem Entfernen eines Eintrages zurückgesetzt wird.
PData := FDataClass.ExtractFirst;
if Assigned(PData) then begin try Verarbeite(PData^); finally Dispose(PData); end; end; |
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Delphi-Quellcode:
?
repeat
if WaitForSingleObject(FDataClass.NewDataEvent, 100) = WAIT_OBJECT_0 then begin PData := FDataClass.ExtractFirst; if Assigned(PData) then begin repeat try Verarbeite(PData^); finally Dispose(PData); end; PData := FDataClass.ExtractFirst; until not Assigned(PData); end; end; until Terminated; end; Den Event würde ich drin lassen um die CPU zu entlasten wenn es gerade mal nichts zu tun gibt und ansonsten sofort loszulegen. Man könnte auch "INFINITE" warten, dann muss aber ins Destroy auf jeden Fall ein SetEvent um den Thread abzubrechen. |
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
OK, wenn das Event drinnenbleiben soll und man keine zusätzliche Schleife möchte, dann halt das Event nur zurücksetzen, wenn nichts mehr in der Liste steht.
Delphi-Quellcode:
function TDataClass.ExtractFirst: POneData;
begin Lock; try if Assigned(FFirst) then begin Result := FFirst; FFirst := FFirst^._Next; if not Assigned(FFirst) then ResetEvent(FEvent); end else Result := nil; finally Unlock; end; end; |
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Moin !
Dann will ich mal das ganze Kostrukt beschreiben um das es geht ... Bei uns geht es um die Verarbeitung von (zumeist seriellen - RS232/USB) Daten. Diese Daten kommen z.B. von einem Datenlogger und liefern - sagen wir mal Spannung, Strom, Höhe. Die Schnittstelle (bzw. die Komponente dazu) ist in dem Fall dann also der Datenlieferant und benutzt das TDataClass.Add um Daten anzulegen. Ob diese Daten dann schon umgerechnet sind von einem String hinzu 3 Floats sei an dieser Stelle mal unwichtig. Wenn die Daten dann abgelegt sind, dann soll einmal die Sekunde alles was noch offen in der Kette ist in eine Grafik geschrieben werden. Zusätzlich sollen zwei Threads einmal die Rohdaten (empfangene Strings) in eine Datei schreiben und einmal die umgerechneten Daten ggf. in eine Art CSV. Dann könnte es noch sowas wie Analoganzeigen (Gauges) geben die eben jede 500-1000ms mal den letzten Wert der Kette darstellen sollen als eine Art "Livemonitoring". Gelöscht werden müssen die Daten wenn die Daten in der Grafik gelandet sind und die beiden Dateien geschrieben sind. Soweit mal die Idee hinter dem ganzen. |
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. |
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Moin !
Zitat:
Zitat:
Zitat:
Die Daten werden mittels Thread abgeholt, verarbeitet und bereitgestellt. Und die Daten liegen in einer Liste vor die eben dynamisch anwachsen kann. Selbst wenn nun der MainThread mal blockt und somit die TChart Befüllung hängt, so baut sich im Hintergrund dennoch die Liste (dank Threads) weiter auf. Und wenn der MainThread wieder "Luft" hat, würde ja auch der rest verarbeitet. Oder sehe ich das gerade falsch ? |
Re: Liste (ähnlich dyn Array) mit fortlaufendem Index?
Liste der Anhänge anzeigen (Anzahl: 1)
Moin !
So ich habe noch ein bisserl getestet. Habe die Demo auch auf MDI ausgebaut. Und das läuft wirklich gut. Man kann enorme Datenmengen reinschaufeln und der Output Thread packt dann alles in das Chart nach und nach. Also ich habe hier mal mit 500.000 Werten in knapp 5 Sekunden am Eingang getestet und das geht stressfrei über die Bühne. Bin echt beeindruckt. :thumb: Und das auch noch trotz Synchronize. Ich habe auch mal additiv einen Button auf das MDI Main gelegt wo ich eine mehr oder minder sinnlose CPU Killerschleife laufen lasse. Und selbst dann funktioniert der Eingang sauber weiter. Und wenn die Last weg ist arbeiten die Output Threads auch wieder mit dem Chart. Wunderbar. Muss heute Abend nen Bier trinken auf euch :hi: :cheer: :cheers: Werde dann heute noch den Input Thread durch eine reale Eingangsquelle ersetzen (RS232). Aber ich sehe da kein großes Problem. Zumal die Datenflut ja bei RS232 eher gering ist :-D Falls jemand noch Verbesserungen zu vermerken hat ... Nur her damit. Ich habe jedenfalls jetzt erstmal einen sehr brauchbaren Stand. Und ich möchte mich nochmal ganz herzlich bei Tryer & himitsu bedanken für die Umsetzung der verketteten Pointerliste. :dp: |
Alle Zeitangaben in WEZ +1. Es ist jetzt 14:35 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