|
Antwort |
Registriert seit: 23. Dez 2007 Ort: Schwarzwald 505 Beiträge Delphi 12 Athens |
#1
Delphi-Version: 10 Seattle
Hallo,
seit Monaten will ich mich an dieses Thema wagen. Ich habe versucht unterschiedlichste Informationen zu berücksichtigen und habe es auch geschafft dass das Ganze läuft. Bei dem gezeigten Beispiel geht es zunächst nur um Grundsätzliches. Natürlich wird der Thread später in eine eigene Unit verschoben und auch die Masterdaten entsprechend gehandelt. Was soll das Programm machen:
Delphi-Quellcode:
Jetzt meine Fragen:
unit fo_ShowData;
{ ****************************************************************************** fo_ShowData Programm um folgende Dinge zu testen * Daten per Thread auslesen und anzeigen -------------------------------------------------------------------------------- Historie 23.01.17, V1.0.1, GPA, Delphi XE10 * Verwaltung der Daten in TList unter Verwendung von TMonitor.Enter/Exit 23.01.17, V1.0.0, GPA, Delphi XE10 * Verwaltung der Daten in TThreadList unter Verwendung von LockList/UnlockList ****************************************************************************** } interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, ActiveX, Generics.Collections, Generics.Defaults, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Data.DB, VclTee.TeeGDIPlus, VclTee.Chart, VclTee.TeEngine, VclTee.Series, Vcl.StdCtrls, VclTee.TeeProcs, VclTee.DBChart, Vcl.Grids, Vcl.DBGrids, VirtualTrees, Vcl.ExtCtrls, Vcl.ComCtrls, Nav, MemDS, DBAccess, Uni, UniProvider, SQLServerUniProvider, VclTee.TeeXML, VclTee.TeeSeriesTextEd, VclTee.TeeURL, VclTee.TeeExcelSource; type TForm2 = class(TForm) paTop: TPanel; paData: TPanel; StatusBar1: TStatusBar; paManuell: TPanel; vstData: TVirtualStringTree; chForce: TChart; Series3: TLineSeries; Series4: TLineSeries; UniConnection1: TUniConnection; SQLServerUniProvider1: TSQLServerUniProvider; UniQuery1: TUniQuery; DataSource1: TDataSource; DBNavigatorSpec1: TDBNavigatorSpec; laReport: TLabel; procedure FormCreate(Sender: TObject); procedure DataSource1DataChange(Sender: TObject; Field: TField); procedure vstDataGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure vstDataGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer); procedure vstDataInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates); private { Private-Deklarationen } procedure CreateThrReadDb(iID: Integer); public { Public-Deklarationen } procedure SyncReady(bValue: Boolean); end; TThreadBoolean = procedure(bValue: Boolean) of Object; PValues = ^TValues; TValues = class dtTime: TDateTime; rForce, rFtarget, rTemperatur, rHumidity: Real; End; TListData = class(TList<TValues>) end; TThrReadDb = class(TThread) protected procedure Execute; override; private _Ready: Boolean; _ReadyEvent: TThreadBoolean; dbcon: TUniConnection; dbProvider: TSQLServerUniProvider; dbQuery: TUniQuery; procedure syncReadyEvent; public property ReadyEvent: TThreadBoolean read _ReadyEvent write _ReadyEvent; end; var Form2: TForm2; lstData: TListData; thrReadDb: TThrReadDb; bThrReadDbIsRunning: Boolean; implementation {$R *.dfm} procedure TForm2.DataSource1DataChange(Sender: TObject; Field: TField); // Bei jedem Blättervorgang in den Masterdaten Thread zum Auslesen der // Detaildaten erzeugen begin laReport.Caption := format('%s [%d]', [UniQuery1.FieldByName('Nr').AsString, UniQuery1.FieldByName('ID').AsInteger]); CreateThrReadDb(UniQuery1.FieldByName('ID').AsInteger) end; procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction); begin lstData.Free; end; procedure TForm2.FormCreate(Sender: TObject); begin lstData := TListData.Create; UniQuery1.Open; end; procedure TForm2.CreateThrReadDb(iID: Integer); // Thread zum Auslesen der Daten aus DB erzeugen begin // Falls noch ein Thread zum Laden von Daten läuft diesen abbrechen if (bThrReadDbIsRunning) then begin // thrReadDb.dbQuery.BreakExec; // Führt zu Fehlermeldung "operation was canceled" thrReadDb.Terminate; bThrReadDbIsRunning := false end; lstData.Clear; bThrReadDbIsRunning := TRUE; thrReadDb := TThrReadDb.Create(TRUE); thrReadDb.ReadyEvent := Form2.SyncReady; { Create suspended--secondProcess does not run yet. } thrReadDb.FreeOnTerminate := TRUE; { You do not need to clean up after termination. } thrReadDb.Priority := tpLower; // Set the priority to lower than normal. if not assigned(thrReadDb.dbQuery) then begin thrReadDb.dbcon := TUniConnection.Create(NIL); thrReadDb.dbcon.ProviderName := TSQLServerUniProvider.Create(NIL).Name; thrReadDb.dbcon.ConnectString := 'Provider Name=SQL Server;Data Source=PsServer;Initial Catalog=ADatabase;Port=0;User ID=sa;Password=THREAD'; thrReadDb.dbQuery := TUniQuery.Create(NIL); thrReadDb.dbQuery.Connection := thrReadDb.dbcon; end; if thrReadDb.dbQuery.Active then thrReadDb.dbQuery.Close; thrReadDb.dbQuery.SQL.Text := format('select * from Cal_force where CalReportID = %d order by ID', [iID]); thrReadDb.Resume; { thread starten } end; procedure TForm2.SyncReady(bValue: Boolean); // Daten der kompletten Liste VST und TChart zuweisen var i: Integer; Values: TValues; begin vstData.NodeDataSize := SizeOf(TValues); vstData.BeginUpdate; vstData.Clear; System.TMonitor.Enter(lstData); try vstData.RootNodeCount := lstData.Count; finally System.TMonitor.Exit(lstData); end; vstData.EndUpdate; Series3.Clear; Series4.Clear; System.TMonitor.Enter(lstData); try Values := TValues.Create; for i := 0 to lstData.Count - 1 do begin Values := lstData.Items[i]; Series3.AddXY(Values.dtTime, Values.rForce); Series4.AddXY(Values.dtTime, Values.rFtarget); end; finally System.TMonitor.Exit(lstData); end; end; procedure TForm2.vstDataGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer); begin NodeDataSize := SizeOf(TValues); end; procedure TForm2.vstDataGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); // Daten in Zellen schreiben var Data: PValues; begin Data := Sender.GetNodeData(Node); case Column of 0: CellText := FormatDateTime('yyyy-mm-dd hh.mm.ss.zzz', Data^.dtTime); 1: CellText := format('%7.2f', [Data^.rForce]); 2: CellText := format('%7.2f', [Data^.rFtarget]); end; end; procedure TForm2.vstDataInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates); // Den Knoten in VST die Daten von lstData zuweisen var Data: PValues; begin Data := Sender.GetNodeData(Node); begin System.TMonitor.Enter(lstData); try Data^ := lstData[Node^.Index]; finally System.TMonitor.Exit(lstData); end; end; end; { ======================== TThrReadDB ======================================== } procedure TThrReadDb.Execute; // Zuerst Query öffnen und dann alle Daten in lstData ablegen var Values: TValues; iT, iT1 : LargeInt; begin inherited; bThrReadDbIsRunning := true; CoInitialize(nil); try thrReadDb.dbQuery.Open; while not thrReadDb.Terminated and not thrReadDb.dbQuery.eof do begin Values := TValues.Create; Values.dtTime := thrReadDb.dbQuery.FieldByName('ErstDat').Value; Values.rForce := thrReadDb.dbQuery.FieldByName('Fact').Value; Values.rFtarget := thrReadDb.dbQuery.FieldByName('Ftarget').Value; System.TMonitor.Enter(lstData); // Liste gegen Zweitzugriff sperren try lstData.Add(Values); finally System.TMonitor.Exit(lstData); // Sperrung der Liste wieder aufheben end; thrReadDb.dbQuery.Next; end; if not thrReadDb.Terminated then begin _Ready := true; Synchronize(syncReadyEvent); end; finally CoUnInitialize; bThrReadDbIsRunning := false; end; end; procedure TThrReadDb.syncReadyEvent; // Formular benachrichtigen, dass Daten komplett in lstData vorliegen begin if assigned(_ReadyEvent) then _ReadyEvent(_Ready); end; end.
Danke Gerd Geändert von norwegen60 (23. Jan 2017 um 14:49 Uhr) |
Zitat |
Registriert seit: 24. Okt 2006 Ort: Seifhennersdorf / Sachsen 5.388 Beiträge Delphi 12 Athens |
#2
Hallöle...
Ich lehne mich mal weit aus dem Fenster ohne alles nachgelesen zu haben... [Nur meine Meinung...über vieles läßt sich streiten ] 1. Hat das einen Sinn das verschiede Variablen den_ Unterstrich tragen statt wie "üblich" F? 2. Warum hat das Event für den Thread den Namen "TThreadBoolean" statt einem sprechenden Namen wie "TFinishLoadList" ... am Namen sollte man schon die Funktionsweise "erahnen" 3. Die Connection gehört in den Thread... 4. Das gehört nicht in den Thread thrReadDb.ReadyEvent := Form2.SyncReady; Den Eventhander in der Form deklarieren und verbinden. 5. TListData = class(TList<TValues>) ... erzeugt dir Speicherlecks mit Values := TValues.Create; besser http://docwiki.embarcadero.com/Libra...ectList.Create 6. Keine Passworte hardcodiert im Connectionstring 7. thrReadDb.Resume; ... nicht mehr verwenden sondern START 8. thrReadDb.FreeOnTerminate := TRUE; habe ich fast überlesen zwischen den Kommentaren. 9. thrReadDb.dbcon.ProviderName := TSQLServerUniProvider.Create(NIL).Name; ... einfach zuordnen statt von neuer Instanz. (imho legt die UniDAC automatisch intern eine Instanz an) besser: FConnection.ProviderName := 'Interbase'; 10. Aufteilung in separate Units. Die Logik (Thread) hat nichts in der Form Unit zu suchen. 11. WICHTG: Der Code ist nicht konsistent. Mal "true" mal "TRUE". Benutze lieber einen Codeformatter... Geändert von haentschman (24. Jan 2017 um 07:44 Uhr) |
Zitat |
Registriert seit: 23. Dez 2007 Ort: Schwarzwald 505 Beiträge Delphi 12 Athens |
#3
Hallo,
vielen Danke fürs Feedback. Ich weiß dass es mühsam ist, den ganzen Code anzuschauen. Wenn ich aber 10 Threads mit unterschiedlichen Fragen öffne wirds wohl auch nicht einfacher. Deshalb habe ich mal alles in einen Topf geworfen (und auch in eine Unit ) 1. Hat das eine Sinn das verschiede Variablen den_ Unterstrich tragen statt wie "üblich" F?
2. Warum hat das Event für den Thread den Namen "TThreadBoolean" statt einem sprechenden Namen wie "TFinishLoadList" ... am Namen sollte man schon die Funktionsweise "erahnen"
4. Das gehört nicht in den Thread thrReadDb.ReadyEvent := Form2.SyncReady;
Den Eventhander in der Form deklarieren und verbinden.
5. TListData = class(TList<TValues>)
... erzeugt dir Speicherlecks mit Values := TValues.Create;
besser http://docwiki.embarcadero.com/Libra...ectList.Create
Delphi-Quellcode:
TCalReport = class
private FNr : String; ... FValues: TListData public ... end; 6. Keine Passworte hardcodiert im Connectionstring
7. thrReadDb.Resume;
... nicht mehr verwenden sondern START
8. thrReadDb.FreeOnTerminate := TRUE;
habe ich fast überlesen zwischen den Kommentaren.
9. thrReadDb.dbcon.ProviderName := TSQLServerUniProvider.Create(NIL).Name;
... einfach zuordnen statt von neuer Instanz. (imho legt die UniDAC automatisch intern eine Instanz an) besser: FConnection.ProviderName := 'Interbase';
10. Aufteilung in separate Units. Die Logik (Thread) hat nichts in der Form Unit zu suchen.
11. WICHTG: Der Code ist nicht konsistent. Mal "true" mal "TRUE". Benutze lieber einen Codeformatter...
Es ist nicht ganz einfach Quellen zu finden, auf die man sich verlassen kann. Auch die Delphi-Beispiele verwenden z.B. Resume http://docwiki.embarcadero.com/CodeE...dList_(Delphi) Und im Moment habe ich noch meine liebe Mühe mit dem Thema "Thread" Grüße Gerd Geändert von norwegen60 (23. Jan 2017 um 23:50 Uhr) |
Zitat |
Registriert seit: 24. Okt 2006 Ort: Seifhennersdorf / Sachsen 5.388 Beiträge Delphi 12 Athens |
#4
Hallöle...
Zitat:
8. thrReadDb.FreeOnTerminate := TRUE; habe ich fast überlesen zwischen den Kommentaren.
Was meinst du damit?
Zitat:
Habe ich mir angeschaut. Hintergrung für TListData = class(TList<TValues>) ist, dass ich die Liste dem Master-Record anhängen will. In etwas so:
Zitat:
2. Warum hat das Event für den Thread den Namen "TThreadBoolean" statt einem sprechenden Namen wie "TFinishLoadList" ... am Namen sollte man schon die Funktionsweise "erahnen"
Geändert von haentschman (24. Jan 2017 um 08:15 Uhr) |
Zitat |
Ansicht |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
ForumregelnEs 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
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
LinkBack URL |
About LinkBacks |