![]() |
Datenbank: MySQL • Version: 5.7.11 • Zugriff über: UniDac
SQL Query in Thread wie Datenrückgabe realisieren
Moin,
ich denke das Thema ist schon immer wieder mal behandelt worden nur irgendwie nicht so wie ich es gerade brauche. Es geht darum, dass ich im Moment versuche sämtliche Query's in einem Thread auszulagern, da mir die Programmoberfläche zu oft kurze Zeit einfriert. Abgesehen davon dachte ich mir, dass es ggf. nicht die schlechteste Wahl ist, die eigentlichen Daten irgendwo zwischen zu speichern und nicht unmittelbar an die Oberfläche zu hängen, so dass ich eventuell, sollte sich die Oberfläche mal ändern, ich nicht an bestimmte Komponenten gebunden bin. Jetzt stellt sich aber die Frage wie gebe ich die Daten aus dem separaten Thread zurück an den Main Thread. Ich beschreibe mal grob was ich gerade gemacht habe: Komponenten die benutzt werden: Unidac DxGrid 1. Separaten Thread angelegt. Dem Thread übergebe ich im Create als Parameter den SQL Query welcher ausgeführt werden soll. Des weiteren werden hier alle benötigten Komponenten erstellt: UniConnection, UniProvider, UniQuery. 2. Im Execute des Threads wird das Uniquery mit Execute gestartet, welches dann mit den Daten die zurück kommen gefüllt wird, sofern der SQL Query erfolgreich ausgeführt werden konnte. Tja und nun gehts los, wie übergebe ich die Daten jetzt an ein Dataset im Main Thread? Genau hier hakt es, da ich ja nicht direkt auf Komponenten im Main Thread zugreifen darf (max. über Syncronize oder Messages), sollte ich jetzt z.B. eine Liste oder Collection erstellen, welche die Daten aufnimmt und diese dann an den Main Thread übergeben oder welche Möglichkeiten habe ich? Die Idee war eigentlich folgende mit z.B. der TCollection. Wenn diese die Felddefinitionen der Tabelle(Feldname, Type, Größe) sowie die Daten darin enthält, könnte ich z.B. diese Daten direkt in der Collection verwalten (Daten löschen, anlegen usw. und damit dann ein z.B. ein TSimleDataset füllen und an ein DBGid oder CXGrid übergeben) danach wird halt ein SQL Query an den SQL Server gesendet mit dem was gerade passiert ist (löschen, anlegen update usw.) Macht man das so, oder gibt es da bessere oder schnellere Lösungen, wie macht Ihr das? |
AW: SQL Query in Thread wie Datenrückgabe realisieren
Wenn Du unidac nutzt, hast die die VirtualTable Komponente dabei, die ist mit nichts verbunden, sollte also Threadsafe sein.
Nach dem Threadstart aber noch vor dem Resume gibst du dem Thread object eine TVirtualTable mit und nach dem Query macht du ein VirtualTable1.Assign(UniQuery1); und dann sendest du deinem Mainthread eine message, dass die daten da sind und beendest den thread. Stell aber sicher, dass die virtualtable nicht genutzt wird, nur vor dem Thread.Resume ud wenn die Nachricht reinkam, dass die daten da sind. Helge Edit: da fällt mir ein, da Du ja dxgrid nutzt, schau dir mal den Servermode an, den gibt es auch für unidac, der lädt nur, was gerade an daten gebraucht wird um das grid zu füllen. Dadurch kannst du 1 mio datensätze haben, der lädt in 1 sekunde ohne probleme. auch beim scrollen wird da nur geladen, was gerade benötigt wird. |
AW: SQL Query in Thread wie Datenrückgabe realisieren
Zitat:
1. Ich soll dem Thread eine Virtualtable mitgeben, ok kann ich machen. 2. Dann soll ich später VirtualTable1.Assign(UniQuery1); ausführen. Woher soll Query1 jetzt gekommen sein, den gibt es nicht, also noch nicht. Der Main Thread besitzt nur das Grid sonst nichts. Alles andere wird ja im separaten Thread ausgeführt (also gibt es dort auch nur das Query. Das mit dem Servermode klingt interessant, aber ist jetzt im Moment noch nicht mein Problem, behalte ich aber mal im Hinterkopf, zur Zeit gehts es nur um die Auslagerung der Querys in einem eigenen Thread und vor allem um die Rückgabe. |
AW: SQL Query in Thread wie Datenrückgabe realisieren
OK, erstmal zum grundsatz : Ich nehme mal an, dass Du die Queries in einen Thread auslagern willst, weil sonst deine app zulange nicht reagiert, oder ?
Ich nehme mal an, dass es so ist ... Der Servermode ist ja deswegen interessant, weil er halt die ergebnislisten so klein hält, dass du keinen thread brauchst, somit wäre dein Problem an der Stelle schon gelöst. Aber gehen wir doch auf den Thread ein : Komponenten die du brauchst sind : TVirtualTable (VT) (Mainthread) TUniConnection, TUniQuery (UniQuery1) (add. Thread) Du erstellst den Thread suspended, gibst ihm die VT und startest den Thread. Der erzeugt die connection und die uniquery componenten, fährt die Abfrage und macht dann das assign zur VirtualTable, damit die VT die Daten hat. Dann schliesst das Query und die Verbindung und sendest deinem Mainthread die NAchricht, dass die Daten fertig sein und der zerstört den thread oder lässt den thread sich zerstören, aber nicht die VT, die kannst Du gleich als Dataset für das grid dann nutzen, aber erst der Datasource zuweisen, wenn der Thread fertig ist, sonst versicht das grid die schon zu lesen während du sie noch befüllst. Verbesserung : Schau dass du vllt nur 100 datensätze erstmal liest um im Grid schon was anzuzeigen und im thread die anderen nachholst. IN firebird geht das mit SELECT FIRST 100 (für die ersten 100) SELECT FIRST 100 SKIP 100 (für die nächsten 100) Bei MySQL kenn ich mich aber ned so aus Ich hoffe, das hilft dir weiter |
AW: SQL Query in Thread wie Datenrückgabe realisieren
Zitat:
Ich bin im Moment nicht zuhause, aber ich werde das Morgen mal versuchen umzusetzen und melde mich dann mal mit deinem kompletten kleinen Projekt zurück, dass ist viel leicht sinnvoller als wenn ich das alles hypothetisch erklären bzw. hat vielleicht jemand anderes ein ähnliches Problem und lernt dann aus meinen Fehlern oder der Umsetzung wie man es machen kann. Bis dahin erstmal vielen Dank für die nützlichen Tips! |
AW: SQL Query in Thread wie Datenrückgabe realisieren
Ok also wie gesagt hab ich mich mal an den Thread gewagt und die Rückgabe in eine VirtualTable gelegt.
1. Problem, ich habe zur schnellen Überprüfung einfach ein DBGrid benutzt, manchmal jedoch nicht immer kommt es zu der Meldung: Canvas erlaubt kein zeichnen, gestoppt wird in VCL.Graphics Zeile 4197 Da weiss ich gerade auch nicht warum der Fehler auftritt. 2. Ein weiteres Problem zu dem es kommen kann und da wird es richtig wild, (Ich hab zum Test das Create, Start des Thread auf einen Button gelegt, wenn dieser aber zu schnell hintereinander geklickt wird haut es mir ziemlich viele Exceptions um die Ohren, angefangen von Blob Fehler bis hin zu (Gitternetz irgendwas, müsste ich noch mal genau nachsehen.) Gut das kann ich schnell umgehen in dem ich einfach den Button so lange sperre wie der separate Thread läuft. In der späteren Hauptanwendung benutze ich ein PageControl, welches beim Seitenwechsel quasi den Thread starten soll um die GUI (Grid und weitere DBControls) mit den Werten zu füllen, da müsste ich mal sehen wie ich das unterbinde das die Seiten zu schnell gewechselt werden. Dann schmeiss ich mal den Code hier rein und hoffe mir kann da jemand auf die Sprünge helfen bezüglich obiger Probleme (Ich geh einfach mal davon aus, mein Thread hat einen Designfehler oder die Hauptanwendung oder beides :shock: ) Die Anwendung:
Delphi-Quellcode:
Der Thread für die SQL Querys:
unit fmView;
interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Data.DB, Vcl.StdCtrls, Vcl.ComCtrls, MemDS, VirtualTable, DBAccess, Uni, Vcl.Grids, Vcl.DBGrids, QueryThread; type TView = class(TForm) DBGrid1: TDBGrid; UniDataSource1: TUniDataSource; VirtualTable1: TVirtualTable; StatusBar1: TStatusBar; MSGLog: TMemo; Label1: TLabel; btnStart: TButton; edtConStr: TEdit; edtSqlTxt: TEdit; QrPB: TProgressBar; procedure btnStartClick(Sender: TObject); procedure FormDestroy(Sender: TObject); private { Private-Deklarationen } FSQLThrd: TSqlQueryThrd; procedure OnThreadStatusMsg( const ThreadStatusMsgPtr: PThreadStatusdMsg); procedure OnThreadQueryStarted( const ThreadQueryStartedMsgPtr: PThreadQueryStartMsg); procedure OnThreadQueryDone( const ThreadQueryDoneMsgPtr: PThreadQueryDoneMsg); procedure OnThreadQueryRecCount( const ThreadQueryRecCountMsgPtr: PThreadQueryRecCountMsg); public { Public-Deklarationen } protected procedure WndProc(var AMsg: TMessage); override; end; var View: TView; implementation {$R *.dfm} procedure TView.btnStartClick(Sender: TObject); begin // I know this will leak at the moment! FSQLThrd := TSqlQueryThrd.Create(self.Handle, edtConStr.Text, edtSqlTxt.Text, VirtualTable1); try FSQLThrd.FreeOnTerminate := false; FSQLThrd.Start; except on E: Exception do MSGLog.Lines.Text := E.Message; end; end; procedure TView.FormDestroy(Sender: TObject); begin if assigned(FSQLThrd) then begin FSQLThrd.Terminate; FSQLThrd.WaitFor; FSQLThrd.Free; end; end; procedure TView.OnThreadQueryStarted( const ThreadQueryStartedMsgPtr: PThreadQueryStartMsg); begin case ThreadQueryStartedMsgPtr^.Running of true: QrPB.State := pbsNormal; false: QrPB.State := pbsPaused; end; Dispose(ThreadQueryStartedMsgPtr); end; procedure TView.OnThreadQueryDone( const ThreadQueryDoneMsgPtr: PThreadQueryDoneMsg); begin case ThreadQueryDoneMsgPtr^.Done of true: begin QrPB.State := pbsPaused; try UniDataSource1.DataSet.DisableControls; UniDataSource1.DataSet := VirtualTable1; // exception // VCL.Graphics line 4197 // EInvalidOperation: Canvas erlaubt kein zeichnen! DBGrid1.DataSource := UniDataSource1; finally UniDataSource1.DataSet.EnableControls; end; end; false: QrPB.State := pbsNormal; end; Dispose(ThreadQueryDoneMsgPtr); end; procedure TView.OnThreadStatusMsg( const ThreadStatusMsgPtr: PThreadStatusdMsg); begin MSGLog.Lines.Add( string.Format('%s %s %s', [ ThreadStatusMsgPtr^.ExecTime, ThreadStatusMsgPtr^.MessageStr, ThreadStatusMsgPtr^.SQLStr ])); Dispose(ThreadStatusMsgPtr); end; procedure TView.OnThreadQueryRecCount( const ThreadQueryRecCountMsgPtr: PThreadQueryRecCountMsg); begin Statusbar1.Panels[0].Text := string.Format('RecCount: %s' , [IntToStr(ThreadQueryRecCountMsgPtr^.ICount)]); Dispose(ThreadQueryRecCountMsgPtr); end; procedure TView.WndProc(var AMsg: TMessage); begin with AMsg do begin case Msg of TS_THREAD_STATUS_AVAILABLE: OnThreadStatusMsg( PThreadStatusdMsg(WParam)); TS_THREAD_QUERY_STARTED: OnThreadQueryStarted( PThreadQueryStartMsg(WParam)); TS_THREAD_QUERY_DONE: OnThreadQueryDone( PThreadQueryDoneMsg(WParam)); TS_THREAD_QUERY_COUNT: OnThreadQueryRecCount( PThreadQueryRecCountMsg(WParam)); end; end; inherited; end; end.
Delphi-Quellcode:
unit QueryThread;
interface uses System.Classes, System.SysUtils, Winapi.Windows, Winapi.Messages, UniProvider, MySQLUniProvider, Data.DB, DBAccess, Uni, MemDS, MemData, VirtualTable; // messages to be send to the main thread const TS_THREAD_STATUS_AVAILABLE = WM_USER + 1; TS_THREAD_QUERY_STARTED = WM_USER +2; TS_THREAD_QUERY_DONE = WM_USER +3; TS_THREAD_QUERY_COUNT = WM_USER +4; type TThreadStatusMsg = record MessageStr: string; SQLStr: string; ExecTime: String; end; PThreadStatusdMsg = ^TThreadStatusMsg; TThreadQueryStartMsg = record Running: Boolean; end; PThreadQueryStartMsg = ^TThreadQueryStartMsg; TThreadQueryDoneMsg = record Done: Boolean; end; PThreadQueryDoneMsg = ^TThreadQueryDoneMsg; TThreadQueryRecCountMsg = record ICount: Int64; end; PThreadQueryRecCountMsg = ^TThreadQueryRecCountMsg; type TSqlQueryThrd = class(TThread) protected procedure Execute; override; procedure OnAfterFetchEvent(DataSet: TCustomDADataSet); procedure OnAfterOpen(DataSet: TDataSet); private FMainHandle: THandle; FSQLText: string; fStatusText: string; FUniDacConnection: TUniConnection; FUniDacSQLQuery: TUniQuery; FVirtualTable: TVirtualTable; ThreadStatusMsgPtr: PThreadStatusdMsg; ThreadQueryStartedMsgPtr: PThreadQueryStartMsg; ThreadQueryDoneMsgPtr: PThreadQueryDoneMsg; ThreadQueryRecCountMsgPtr: PThreadQueryRecCountMsg; function GetLongTime(aTime: TDatetime) : string; public constructor Create(aMainHandle: THandle; aConnectionStr: string; aSQL: string; aVTable: TVirtualTable); overload; destructor Destroy; override; end; implementation { TSqlQueryThrd } constructor TSqlQueryThrd.Create(aMainHandle: THandle; aConnectionStr, aSQL: string; aVTable: TVirtualTable); begin inherited Create(True); // main thread handle FMainHandle := aMainHandle; FUniDacConnection := TUniConnection.Create(nil); FUniDacConnection.ProviderName := 'MySQL'; FUniDacConnection.ConnectString := aConnectionStr; Assert(aConnectionStr <> '', 'Connection-String can not be empty!'); // test if connection is successful otherwise goodbye try try FUniDacConnection.Connect; except terminate; end; finally // can be disconnected the unidac query established a connection by itself if needed FUniDacConnection.Disconnect; end; FSQLText := aSQL; Assert(FSQLText <> '', 'SQL-Text can not be empty!'); FUniDacSQLQuery := TUniQuery.Create(nil); FUniDacSQLQuery.Connection := FUniDacConnection; FUniDacSQLQuery.FetchingAll; FUniDacSQLQuery.AfterFetch := OnAfterFetchEvent; FUniDacSQLQuery.AfterOpen := OnAfterOpen; FUniDacSQLQuery.SQL.Clear; FVirtualTable := aVTable; end; destructor TSqlQueryThrd.Destroy; begin if FUniDacConnection.Connected then FUniDacConnection.Disconnect; FUniDacConnection.Free; FUniDacSQLQuery.Free; inherited Destroy; end; procedure TSqlQueryThrd.Execute; begin NameThreadForDebugging('StormThread'); FUniDacSQLQuery.SQL.Text := FSQLText; // send the main thread some messages... New(ThreadQueryStartedMsgPtr); ThreadQueryStartedMsgPtr^.Running := True; if not(PostMessage(FMainHandle, TS_THREAD_QUERY_STARTED, integer(ThreadQueryStartedMsgPtr), 0)) then begin Dispose(ThreadQueryStartedMsgPtr); terminate; end; New(ThreadStatusMsgPtr); ThreadStatusMsgPtr^.MessageStr := 'Status: Query started'; ThreadStatusMsgPtr^.ExecTime := GetLongTime(Now); ThreadStatusMsgPtr^.SQLStr := ''; if not(PostMessage(FMainHandle, TS_THREAD_STATUS_AVAILABLE, integer(ThreadStatusMsgPtr), 0)) then begin Dispose(ThreadStatusMsgPtr); terminate; end; try try FUniDacSQLQuery.Execute; // Must be set here otherwise the virtualtable in the main thread // does not contain any data! FVirtualTable.Assign(FUniDacSQLQuery); except FUniDacSQLQuery.Close; FUniDacConnection.Close; terminate; end; finally FUniDacSQLQuery.Close; FUniDacConnection.Disconnect; terminate; end; end; function TSqlQueryThrd.GetLongTime(aTime: TDatetime): string; var formattedDate : string; begin Result :=''; try DateTimeToString(formattedDate, 'hh:nn:ss:zz', aTime); finally Result := FormattedDate; end; end; // special for unidac if that event is raised the query is done as written in the docu... procedure TSqlQueryThrd.OnAfterFetchEvent(DataSet: TCustomDADataSet); begin New(ThreadStatusMsgPtr); ThreadStatusMsgPtr^.MessageStr := 'Status: Query done'; ThreadStatusMsgPtr^.ExecTime := GetLongTime(Now); ThreadStatusMsgPtr^.SQLStr := '// ' + FSQLText; if not(PostMessage(FMainHandle, TS_THREAD_STATUS_AVAILABLE, integer(ThreadStatusMsgPtr), 0)) then begin Dispose(ThreadStatusMsgPtr); terminate; end; New(ThreadQueryDoneMsgPtr); ThreadQueryDoneMsgPtr^.Done := True; if not(PostMessage(FMainHandle, TS_THREAD_QUERY_DONE, integer(ThreadQueryDoneMsgPtr), 0)) then begin Dispose(ThreadQueryDoneMsgPtr); terminate; end; end; // this event is raised when the record count is available procedure TSqlQueryThrd.OnAfterOpen(DataSet: TDataSet); begin New(ThreadQueryRecCountMsgPtr); ThreadQueryRecCountMsgPtr^.ICount := FUniDacSQLQuery.RecordCount; if not(PostMessage(FMainHandle, TS_THREAD_QUERY_COUNT, integer(ThreadQueryRecCountMsgPtr), 0)) then begin Dispose(ThreadQueryRecCountMsgPtr); terminate; end; end; end. |
AW: SQL Query in Thread wie Datenrückgabe realisieren
Was mir beim ersten Drüberschauen aufgefallen ist:
Du benutzt Postmessage d.h. es wird nicht gewartet bis die Nachrichten verarbeitet sind. Kann es dadurch zu Problemen kommen weil der VCL-Thread und dein Query-Thread gleichzeitig auf Sachen zugreifen? Du rufst häufig Terminate auf, wertest aber Terminated nicht aus. Terminate beendet den Thread nicht sofort! |
AW: SQL Query in Thread wie Datenrückgabe realisieren
Zitat:
Ich kann wie gesagt mir das Problem im Moment selbst nicht erklären. Wegen dem Terminate geb ich Dir recht, dass ist so völliger Blödsinn. P.s. Was mir noch einfällt, eigentlich sofern ich die Doku von Unidac richtig verstanden habe, greife ich erst dann auf Sachen zu bzw. weise diese zu wenn das Query fertig ist, also nicht vorher. Lt. Doku ist dies der Fall wenn AfterFetch eintritt, siehe Thread. |
AW: SQL Query in Thread wie Datenrückgabe realisieren
Verstehe von dem Thema nicht wirklich was, aber was mich verwundert:
Wenn der Thread fertig ist, weist Du Datasource die VirtualTable zu und dem Grid dann DataSource. Das kann ich nachvollziehen, allerdings hätte ich erwartet, dass vor dem Start des Thread eben gerade diese Verbindung aufgehoben wird. Ich würd' hier also erwarten, dass nach dem ersten Threadstarten für die restliche Laufzeit des Programmes die Verbindung VirtualTable->DataSource->Grid bestehen bleibt. Allerdings hatte ich die bisherige Anforderung so verstanden, dass eben genau das nicht der Fall sein soll. Dashier verstehe ich nicht:
Delphi-Quellcode:
Execute nimmt man bei 'ner Query doch eigentlich, wenn man keine Ergebnismenge erwartet, also bei Insert, Update ...
try
try FUniDacSQLQuery.Execute; // Must be set here otherwise the virtualtable in the main thread // does not contain any data! FVirtualTable.Assign(FUniDacSQLQuery); except FUniDacSQLQuery.Close; FUniDacConnection.Close; terminate; end; Müsste es bei 'nem Select noch Open heißen. Bei 'nem Execute ist doch eigentlich auch kein Close erforderlich. Oder ist das hier in diesem Zusammenhang anders? |
AW: SQL Query in Thread wie Datenrückgabe realisieren
Zitat:
Zitat:
|
AW: SQL Query in Thread wie Datenrückgabe realisieren
Was mich noch interessieren würde:
Muss man die VirtualTable vor der Datenübernahme nicht schließen und nach der Zuweisung auf DataSource nicht öffnen? Sprich: Bekommt man bei
Delphi-Quellcode:
'ne offene Datenmenge zurück?
FVirtualTable.Assign(FUniDacSQLQuery);
Delphi-Quellcode:
procedure TView.btnStartClick(Sender: TObject);
begin UniDataSource1.DataSet.DisableControls; UniDataSource1.DataSet.Close; DBGrid1.DataSource := Nil; UniDataSource1.DataSet := Nil; // I know this will leak at the moment! FSQLThrd := TSqlQueryThrd.Create(self.Handle, edtConStr.Text, edtSqlTxt.Text, VirtualTable1); try FSQLThrd.FreeOnTerminate := false; FSQLThrd.Start; except on E: Exception do MSGLog.Lines.Text := E.Message; end; end; procedure TView.OnThreadQueryDone( const ThreadQueryDoneMsgPtr: PThreadQueryDoneMsg); begin case ThreadQueryDoneMsgPtr^.Done of true: begin QrPB.State := pbsPaused; try UniDataSource1.DataSet.DisableControls; UniDataSource1.DataSet := VirtualTable1; DBGrid1.DataSource := UniDataSource1; UniDataSource1.DataSet.Open; finally // Das würd' ich nur machen, wenn's vorher keine Exception gab. UniDataSource1.DataSet.EnableControls; end; end; false: QrPB.State := pbsNormal; end; Dispose(ThreadQueryDoneMsgPtr); end; |
AW: SQL Query in Thread wie Datenrückgabe realisieren
Zitat:
Zitat:
Auch Dein RecordCount wird eventuell falsch sein, da UniQuery die Zahl anzeigt, die sie erstmal holen (default 25). Entweder musst Du QueryRecordCount property setzen (macht uniquery dann 2x das Query, einmal zum zählen und ein 2. Mal um die ersten Daten zu holen). Ich empfehle einfach den RecordCount der VirtualTable nach dem Assign zu nutzen. Aber da beendest Du den Thread eh und du kannst im MainThread einfach den RecordCount der VT zu fragen. @nahpets : ich glaube, es spielt keine Rolle, da VT eh das dataset schliessen muss um die Field Liste zu löschen, da die ja von DataSet übernommen wird. Und der VT sollte dann automatisch geöffnet werden. Aber das kann man ja überprüfen mit
Code:
If Not VT.State in dsBrowsing Then
|
AW: SQL Query in Thread wie Datenrückgabe realisieren
Zitat:
Teilweise bekomme ich sogar noch folgendes: VCL.Grids stop in Zeile 888 GridIndex ausserhalb des gültigen Bereichs (mal was neues) Was den RecordCount angeht denke ich nicht das dieser falsch ist, ich setze beim Query explizit FetchAll also soll lt. Doku alles an Daten geholt werden und nicht nur partiell. Zitat:
|
AW: SQL Query in Thread wie Datenrückgabe realisieren
Warum nimmst Du die Windows MessageQue und nicht System.Messaging?
Dann kannst Du dir auch das ganze Pointer-Zeug sparen. Warum nimmst Du eine TThread Klasse und nicht System.Threading? Eine eigene Threadklasse nutze ich nur noch, wenn ich den Thread 1x Erzeuge und behalte... Zum Beispiel um mit einen SetEvent(StartEvent) die Verarbeitung in Nano-Sekundenbereich beginnen zu lassen. Zum Beispiel, wenn von der Eingangsqueue ein neuer SQL-Befehlt kommt. Vorgehen: - Ich gebe die SQL-Befehle in die ThreadSaveQueue. Bei Eingang wir der Event gefeuert und der Thread läuft sofort los. - ich kann jederzeit neue Befehle in die Queue feuern und die werden nacheinander abgearbeitet. - Der Thread erzeugt die Daten und packt diese in einen Ausgang-Queue. Damit ist der Thread frei für weitere Aufgaben. - Da ich der Eingangsqueue eine anonyme Procedure mitgegeben habe, kann die Ausgangsqueue in einem 2. Thread nach und nach die Syncronize der anonymen Proceduren aufrufen, die die Daten in der UI darstellen. Natürlich kann man das über die System.Threading auch machen! Ich nutze jedoch eine TThread-Klasse, da ich hierüber den MultiThread-Zugriff auf eine SQLite Datenbank serialisiere! Wenn ich mit MultiThread/MultiConnectionfähigen Datenbanken arbeite, nutze ich natürlich den ThreadPool, um so viele Anfragen wie möglich gleichzeitig zu handeln und die Skalierbarkeit des Datenbankservers auszunutzen. Mavarik |
AW: SQL Query in Thread wie Datenrückgabe realisieren
Zitat:
Das passiert, wenn man auf 'ne Zelle, Spalte, Zeile zugfreift, die es nicht (mehr) gibt. Kann in dem Zusammenhang mit DBGrids passieren, wenn die Spaltenzahl der aktuellen Datenmenge nicht mit der der vorherigen übereinstimmt, das Grid aber trotzdem versucht die Daten einzulesen. Schau mal bitte, ob Dein Grid sowas in der Art kennt:
Delphi-Quellcode:
(Eigentlich braucht man sowas ja nicht, aber manchmal doch ;-))
DBGrid.Fields.Clear;
DBGrid.Columns.Clear; Wenn ja, bau das nach dem Entfernen der Datasource noch ein oder vor der Zuweisung der Datasource, wenn Du die befüllte VT bekommst. |
AW: SQL Query in Thread wie Datenrückgabe realisieren
Zitat:
Also keine Ahnung was das Problem ist, ich hab jetzt im ButtonClick noch extra folgendes eingebaut zur Sicherheit, ändert weiterhin nichts an den Exceptions:
Code:
DBGrid1.Columns.Clear;
VirtualTable1.Open; VirtualTable1.Edit; VirtualTable1.ClearFields; VirtualTable1.Close; DBGrid1.DataSource := Nil; |
AW: SQL Query in Thread wie Datenrückgabe realisieren
Liste der Anhänge anzeigen (Anzahl: 1)
Ich habe Dir mal schnell deins umgebaut, so dass es geht.
Die Zwischennachrichten abgeschaltet, wichtig ist ja erstmal, dass es überhaupt fehlerfrei läuft. Nach dem Start des Threads wird nicht mehr auf die VT zugegriffen bis zum (neu implementierten) OnTerminate event. Die VT ist da noch geschlossen, von daher der Check aufs VirtualTable.Active. Den Provider musst imm Thread nicht setzen, der ist im ConnectionString. Musst ihn mal auf MySQL setzen wieder, ich habe nur Firebird installiert. Ich habe den thread mehrfach ausgeführt ohne Fehler im Grid (nutze Delphi 10.1 Upd1), auch das Programm mehrfach, konte aber den Fehler nicht feststellen, den Du erwähntest. Viel Spass :) Helge |
AW: SQL Query in Thread wie Datenrückgabe realisieren
Zitat:
|
AW: SQL Query in Thread wie Datenrückgabe realisieren
Was ich nochmal erwähnen wollte zum Thema...
1. Es war so, wie ich sagt : nach dem SQL.Execute ist RecordCount bei 25, nach dem Assign auf der Zahl, was du wirklich an Register hast (Assign zieht alle register und dann kommt das natürlich hin). Der RecordCount der VT wird erst gesetzt, wenn sie offen ist (Active = True), sonst ist er 0. Das im Thread ausführen bringt dir keinen Geschwindigkeitsvorteil, da das Query immer noch genauso langsam/schnell ist. Dazu müsstest Du die Daten stückeln, also in Blöcken anfordern. Wie ich vorher im Thread schonmal erwähnt hatte. Helge |
AW: SQL Query in Thread wie Datenrückgabe realisieren
Jetzt wirds ganz lustig :-D
Connection Name "Use Unicode" kennt er nicht. "CharacterSet" auch nicht. Wenn ich das raus nehme (ich denke mal liegt an der verschiedenen Version von Unidac die wir wohl haben) bekomm ich keine Daten :shock: Macht aber erst mal nichts, ich werde das noch mal so umbauen mit meiner Version und dann mal schauen ob es dann geht, Ich denk mal Morgen bin ich damit soweit. Alles klar funktioniert jetzt wunderbar. Nochmals vielen Dank für Deine Mühe! |
AW: SQL Query in Thread wie Datenrückgabe realisieren
Nimm einfach deinen ConenctionString, wie Du ihn vorher genutzt hattest. Wenn der den provider nicht hat, dann setz ihn halt im Thread wieder.
|
AW: SQL Query in Thread wie Datenrückgabe realisieren
Zitat:
Wenn ich mir genau ansehe was Du gemacht hast, denke ich kenne ich auch das Problem jetzt. Das Grid bei mir sah teilweise von den Spalten her etwas seltsam aus, manchmal waren diese komplett zusammen geschoben. So wie ich das sehe, war mein Zugriff in den Messages also sprich AfterFetch viel zu früh. Dein OnTerminate ist denke ich die Lösung gewesen. |
AW: SQL Query in Thread wie Datenrückgabe realisieren
Wichtig ist ja,d ass Du jetzt eine basis hast, auf der Du aufsetzen kannst.
Schau Dir trotzdem mal den SrverMode von DevExpress an, der löst dein Problem besser. |
AW: SQL Query in Thread wie Datenrückgabe realisieren
Zitat:
@Mavarik Das hört sich alles sehr interessant an und ist vermutlich ein sehr sauberer Weg, aber das was Du beschrieben hast sind so zu sagen Böhmische Dörfer für mich ich hab mit TThread schon so meinen Kampf, aber das versteh ich jetzt zumindest teilweise:-D |
AW: SQL Query in Thread wie Datenrückgabe realisieren
Zitat:
|
AW: SQL Query in Thread wie Datenrückgabe realisieren
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:12 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