AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Datenbanken Delphi Firebird embedded mit Threads
Thema durchsuchen
Ansicht
Themen-Optionen

Firebird embedded mit Threads

Ein Thema von stahli · begonnen am 11. Sep 2009 · letzter Beitrag vom 19. Sep 2009
Antwort Antwort
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.336 Beiträge
 
Delphi 11 Alexandria
 
#1

Firebird embedded mit Threads

  Alt 11. Sep 2009, 12:28
Datenbank: Firebird • Version: 2.1 emb • Zugriff über: IBX
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:
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;
Jetzt meine Fragen:
-------------------

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
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.336 Beiträge
 
Delphi 11 Alexandria
 
#2

Re: Firebird embedded mit Threads

  Alt 11. Sep 2009, 20:45
Meine Versuche funktionieren so nicht...

Delphi-Quellcode:
...
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;
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?

Stahli
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.336 Beiträge
 
Delphi 11 Alexandria
 
#3

Re: Firebird embedded mit Threads

  Alt 11. Sep 2009, 22:44
Ich nähere mich an:

Delphi-Quellcode:
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;
...
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...
Ich werde das morgen mal weiter versuchen...
  Mit Zitat antworten Zitat
Blup

Registriert seit: 7. Aug 2008
Ort: Brandenburg
1.429 Beiträge
 
Delphi 10.4 Sydney
 
#4

Re: Firebird embedded mit Threads

  Alt 14. Sep 2009, 07:23
Zitat von stahli:
    ThreadIBDatabase.SetHandle(IBDatabase.Handle);
Eine Datenbankverbindung wird von zwei Datenbankverbindungskomponenten verwaltet?
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.
  Mit Zitat antworten Zitat
Funky-Sepp

Registriert seit: 14. Okt 2003
Ort: Freudenricht/Velburg
56 Beiträge
 
#5

Re: Firebird embedded mit Threads

  Alt 15. Sep 2009, 07:42
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 Connection Pool.

Hab mal ein bisschen danach gesucht auch was gefunden, aber nichts für IBX (glaube ich )

IB Expert

mit welcher Delphi Version arbeitest du?
Wolfgang
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.336 Beiträge
 
Delphi 11 Alexandria
 
#6

Re: Firebird embedded mit Threads

  Alt 15. Sep 2009, 11:24
@Blup:
Das mit dem SetHandle habe ich hier gefunden.
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
  Mit Zitat antworten Zitat
Blup

Registriert seit: 7. Aug 2008
Ort: Brandenburg
1.429 Beiträge
 
Delphi 10.4 Sydney
 
#7

Re: Firebird embedded mit Threads

  Alt 15. Sep 2009, 14:42
Zitat von stahli:
@Blup:
Das mit dem SetHandle habe ich hier gefunden.
Du meinst also, dass es gar nicht geht?
So jedenfalls nicht. Wenn sich zwei unabhängige Connections (eigenes Handle) innerhalb der Anwendung öffnen lassen, kann jeder Thread eine eigene bekommen.

Andernfalls müssen alle Datenbankzugriffe und Zugriffe auf Datenbankkomponenten serialisiert werden (TCriticalSection).
Im Prinzip so:
Code:
CriticalSection.Enter
Transaktion starten
Daten lesen oder schreiben
Transaktion beenden
CriticalSection.Leave
Unter dieser Bedingung können sich alle Threads eine Verbindung teilen.
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.336 Beiträge
 
Delphi 11 Alexandria
 
#8

Re: Firebird embedded mit Threads

  Alt 19. Sep 2009, 22:14
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
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es 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

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 11:08 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz