![]() |
Datenbank: Firebird • Version: 2.1 emb • Zugriff über: IBX
Firebird embedded mit Threads
Hallo DB-Profis,
folgende Situation: ------------------- Ich greife mit eigenen Komponenten über eigene SQL-Funktionen auf eine FB-Datenbank zu. Die SQL-Funktionen fordern aus einem Pool eine (oder mehrere) freie IBSql an, deklarieren eine passende SQL-Klausel, führen sie aus und geben sie zur Freigabe wieder an den Pool zurück. Bevor der Pool eine freie IBSql herausgibt (und ggf. vorab bei Bedarf eine eine neue erzeugt) wird eine Standard-IBDatabase und IBTransaction zugewiesen. Das funktioniert auch wunderbar :-) Nun gibt es aber eine etwas zeitintensive Funktion (TMyComponent.Compare), die 1-2 Sekunden dauern kann und im Ergebnis einiger SQL-Abfragen ein paar Schalter aktiviert bzw. deaktiviert. Dies wollte ich daher in einem Thread ausführen, damit der Anwender in der Zeit z.B. schon mal weiter tippen kann. Dies führt jedoch zu Problemen, insbesondere zu der Fehlermeldung "invalid request handle". Werden die SQL-Funktionen im Rahmen eines solchen Threads ausgeführt, muss ich dann wohl statt der Standard-IBDatabase eine Thread-eigene IBDatabase verwenden (soweit ich gelesen habe)... Überlegung: ----------- Ich muss also in der Pool-Komponente beim Bereitstellen einer freien IBSql unterscheiden, ob gerade "die Anwendung" oder "ein Thread" für die Aktion zuständig ist. Das sollte dann in etwa so aussehen:
Delphi-Quellcode:
Jetzt meine Fragen:
function TMyPool.IBSql: TIBSql;
var IBSql: TIBSql; MyThread: TThread; MyIBDatabase: TIBDataBase; begin IBSql := FreieOderNeueIBSql; MyThread := CurrentThread; // Instanz eines selbst gestarteten Thread ermitteln if not Assigned(MyThread) then // Hauptprozess läuft begin IBSql.Database := StandardIBDatabase; IBSql.Transaction := StandardIBTransaction; end else begin // eigener Thread läuft MyIBDatabase := TIBDataBase.Create(MyThread); MyIBDatabase.StartTransaction; IBSql.Database := MyIBDatabase; IBSql.Transaction := MyIBDatabase.Transaction; end; Result := IBSql; end; ------------------- 1) Sind so überhaupt Thread-Zugriffe auf eine FB-EMBEDDED-DB möglich oder gibt es vielleicht eine einfachere Lösung? 2) Muss der jeweilige Thread Owner der "neuen IBDatabase" sein (sonst kann ich evtl. auf die Instanzsuche des Thread verzichten und lediglich auswerten, OB ein Thread läuft)? 3) Gibt es eine bessere Möglichkeit, die Thread-Instanz zu ermitteln, als diese in einer eigenen Liste zu sammeln und zu suchen (ich starte die Threads immer selbst)? 4) GetCurrentThread liefert mir immer ein Handle <> 0, auch wenn kein "externer" Thread gestartet wurde (sicher ist das das Handle des Anwendungsprozesses). Wie kann ich erkennen, ob es sich bei dem Handle um eine "extra gestarteten" Thread außerhalb des Hauptprozesses handelt (egal ob von mir gestartet oder z.B. von einer Fremdkomponente) -> CurrentHandleIsNotMainProcess 5) Was bedeutet "Pseudo-Handle" in dem Zusammenhang? Kann ich mit GetCurrentThread eigentlich unterschiedliche Instanzen des gleichen Thread(typs) finden? Danke Stahli |
Re: Firebird embedded mit Threads
Meine Versuche funktionieren so nicht...
Delphi-Quellcode:
Im Test wurde genau 1 Thread erzeugt, der auch in der Liste enthalten ist. Die Handles der Vergleichsfunktion sind jedoch nicht identisch. Wie kann ich erkennen, dass der betreffende Thread gerade "zuständig" ist?
...
dcExportImport.Compare; // so funktioniert alles, aber ohne Thread ... CompareThread := TCompareThread.Create(dcExportImport); // so will ich Compare in einem Thread ausführen ... { TCompareThread } constructor TCompareThread.Create(Value: TdcExportImport); begin ThreadList.Add(Self); // der Thread wird in eine Liste eingetragen dcExportImport := Value; FreeOnTerminate := True; inherited Create(False); end; procedure TCompareThread.Execute; begin dcExportImport.Compare; // und Compare im Thread ausgeführt end; ... function TdcQuerys.IBSql: TIBSql; var I: Integer; TmpIBSql: TIBSql; Thread: TThread; begin // der "Pool" stellt eine freie oder neue IBSql bereit ... Thread := CurrentThread; // UND SOLLTE HIER ERMITTELN, OB EIN THREAD AKTIV IST if not Assigned(Thread) then begin TmpIBSql.Database := IBDatabase; TmpIBSql.Transaction := IBTransaction; end else begin Beep; TmpIBSql.Database := IBDatabase; TmpIBSql.Transaction := IBTransaction; end; Result := TmpIBSql; end; ... function CurrentThread: TThread; var Thread: TThread; I: Integer; H: THandle; begin Result := nil; for I := 0 to ThreadList.Count - 1 do begin Thread := TThread(ThreadList[I]); if Assigned(Thread) then begin H := GetCurrentThread; if Thread.Handle = H then // hier hätte ich erwartet, begin // dass der einzige erzeugte Result := Thread; // Tread hier gefunden wird Exit; // H hat jedoch den Wert=4294967294 end; // und Thread.Handle=652 end; end; end; Stahli |
Re: Firebird embedded mit Threads
Ich nähere mich an:
Delphi-Quellcode:
Offenbar werden die SQL-Routinen innerhalb des Threads jetzt korrekt ausgeführt. Jetzt treten aber Fehler auf, wenn der Thread beendet ist und wieder der "normale" Weg beschritten wird...
function CurrentThread: TThread;
var Thread: TThread; I: Integer; ID: Word; begin Result := nil; ID := GetCurrentThreadId; for I := 0 to ThreadList.Count - 1 do begin Thread := TThread(ThreadList[I]); if Assigned(Thread) then begin if Thread.ThreadID = ID then // der ID-Vergleich funktioniert begin Result := Thread; // der laufende Thread wird gefunden Exit; end; end; end; end; var I: Integer; TmpIBSql: TIBSql; Thread: TThread; ThreadIBDatabase: TIBDatabase; ThreadIBTransaction: TIBTransaction; ... Thread := CurrentThread; if not Assigned(Thread) then begin TmpIBSql.Database := IBDatabase; // dies wird "normalerweise" ausgeführt TmpIBSql.Transaction := IBTransaction; end else begin Beep; // dies wird ausgeführt, wenn ein Thread läuft :-) ThreadIBDatabase := TIBDatabase.Create(Self); ThreadIBDatabase.SetHandle(IBDatabase.Handle); ThreadIBTransaction := TIBTransaction.Create(Self); ThreadIBDatabase.DefaultTransaction := ThreadIBTransaction; ThreadIBTransaction.StartTransaction; TmpIBSql.Database := ThreadIBDatabase; TmpIBSql.Transaction := ThreadIBTransaction; end; Result := TmpIBSql; ... Ich werde das morgen mal weiter versuchen... |
Re: Firebird embedded mit Threads
Zitat:
Das kann nicht gut gehen, erst recht nicht mit Threads. Spätestens wenn eine Komponente die Verbindung schließt, ist das Handle ungültig. Jeder Thread braucht eine eigene Verbindung, nicht nur eine eigene Verbindungskomponente. |
Re: Firebird embedded mit Threads
Hi Stahli,
wenn ich das richtig verstehe was du da machen willst wäre doch ein Firebird Connection Pool genau das richtige. Leider mach ich mit Delphi nicht mehr viel, deshalb bin ich da nicht auf den laufenden aber in .Net gibt es für sowas einen ![]() Hab mal ein bisschen danach gesucht auch was gefunden, aber nichts für IBX (glaube ich :gruebel:) ![]() mit welcher Delphi Version arbeitest du? |
Re: Firebird embedded mit Threads
@Blup:
Das mit dem SetHandle habe ich ![]() Du meinst also, dass es gar nicht geht? @Funky-Sepp: Ich möchte möglichst nichts anderes als die IBX verwenden. Meine Komponenten und Funktionen definieren SQL-Statements und holen bzw. schreiben so bei Bedarf ihre Daten. Solange das liniar nacheinander passiert ist alles perfekt. Einige zeitlich aufwendige Funktionen möchte ich aber gern in einem eigenen Thread laufen lassen. Das bekomme ich bisher nicht hin. Stahli |
Re: Firebird embedded mit Threads
Zitat:
Andernfalls müssen alle Datenbankzugriffe und Zugriffe auf Datenbankkomponenten serialisiert werden (TCriticalSection). Im Prinzip so:
Code:
Unter dieser Bedingung können sich alle Threads eine Verbindung teilen.
CriticalSection.Enter
Transaktion starten Daten lesen oder schreiben Transaktion beenden CriticalSection.Leave |
Re: Firebird embedded mit Threads
Mit Threads habe ich das Problem nicht hinbekommen.
Grundsätzlich habe ich das inzwischen so verstanden, dass es mit FB embedded mit unsycronisierten Threads (also wirklich "parallel") gar nicht geht. Aber auch mit dem FB-Server ist es mir nicht gelungen. Ich nutze jedoch inzwisdchen eine andere Lösung, die auch mit FB embedded funktioniert. Ich sammle die Komponenten, die "Compare" durchführen sollen in einer Liste und arbeite diese dann nach und nach im OnIdle ab. Das funktioniert überraschend gut - mein Problem ist damit gelöst. Danke an die Helfer. Stahli |
Alle Zeitangaben in WEZ +1. Es ist jetzt 12:30 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