Delphi-PRAXiS
Seite 1 von 3  1 23      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Thread freigeben, wenn er festklebt :) (https://www.delphipraxis.net/184505-thread-freigeben-wenn-er-festklebt.html)

MyRealName 31. Mär 2015 17:39

Delphi-Version: 5

Thread freigeben, wenn er festklebt :)
 
Hallo,

hab mal eine Frage...
Ich lagere gerade einige DB Prozesse in einen service aus, welcher wiederrum auf dem Server läuft. Dazu habe ich mir einen ThreadPool gebaut und Datenbanken klassifiziert, ob sie in einem Thread allein laufen (weil viel genutzt), oder ob sie ihn sich mit 3 oder 15 DBs teilen (Also max DBs ist 1, 4 oder 16 bis jetzt)
In denen führe ich eine stored procedure aus (über UniDAC), welche dann entscheidet, welche Aufgabe erledigt werden soll. Soweit, so gut.. das geht auch alles sehr schön und flink, auch die Erzeugung und Schliessung von Threads.
Jetzt kommt der theoretische Fall den ich bedenken muss :

Wenn die Stored Procedure aus irgendwelchen Gründen mal nicht zurück kommt, klebt sich der thread ja fest (und wenn er geshared ist mit anderen DBs, wird bei denen ja auch nichts mehr ausgeführt). Dazu habe ich ein Ping Pong eingebaut über Windows Nachrichten, welche alle 30 sekunden die threads fragt, ob sie noch leben. Heisst, wenn ein thread nicht antwortet, schlägt der timer nach 30 sekunden wieder auf und erkennt, dass der thread nicht geantwortet hat und soll ihn beenden und neu erstellen (habe alle information, um ihn neu zu starten).

Problem ist, beim Thread.Free bleibt alles stehen dann. wenn threads sich normal dort beenden (weil der letzte User raus ist), geht alles toll. Aber ich kann ja nicht Terminated auf true setzen, weil der thread ja blockiert ist.

Gibt es ideen ?


Code:
    Try
      UsageLevel := vtThreadDataUsageLevel.Value;
      OldThreadId := vtThreadDataThreadId.Value;
      EnterCriticalSection(aCriticalSection);
      FpThread := TDatabaseConnectionThread(Pointer(vtThreadDataaObj.AsInteger));
      FpThread.Free;
      LeaveCriticalSection(aCriticalSection);
    except
      On E: Exception Do
        CodeSite.Send( csmLevel1, 'Error deleting thread', E.Message);
    End;

BUG 31. Mär 2015 17:54

AW: Thread freigeben, wenn er festklebt :)
 
Um die beste Möglichkeit direkt auszuschließen: Die UniDAC-Komponenten bieten keinen Timeout für einen Request an?
Leider musst du davon ausgehen, das die Datenbankverbindung (oder die gesamte Bibliothek) nicht mehr korrekt funktioniert kann, wenn du den ausführenden Thread zwischendurch einfach abschießt :|

Außerdem springt mir ins Auge, dass du deine Critical Section nie freigibst, wenn im Folgenden ein Fehler auftritt:
Delphi-Quellcode:
      FpThread := TDatabaseConnectionThread(Pointer(vtThreadDataaObj.AsInteger));
      FpThread.Free;
Was schützt die CS denn genau?

Sir Rufo 31. Mär 2015 17:59

AW: Thread freigeben, wenn er festklebt :)
 
Zitat:

Zitat von BUG (Beitrag 1295605)
Außerdem springt mir ins Auge, dass du deine Critical Section nie freigibst, wenn im Folgenden ein Fehler auftritt:
Delphi-Quellcode:
      FpThread := TDatabaseConnectionThread(Pointer(vtThreadDataaObj.AsInteger));
      FpThread.Free;
Was schützt die CS denn genau?

Ich vermute, dass ist eine CS die als Argument übergeben wird (wegen aCriticalSection)

himitsu 31. Mär 2015 18:07

AW: Thread freigeben, wenn er festklebt :)
 
Was heißt denn hängen/festkleben?
Bzw. warum hängt der denn?

Gerade in einem Service, vorallem wenn der länger läuft, sollte man beser alles ordentlich beenden und vorallem die Resourcen richtig freigeben, was durch Abschießen praktisch nicht möglich ist.


PS: Da du natürlich keine Resourcenschutzblöcke verbaut hast, gibt es bei einer Exception einen geilen Deadlock in deiner aCriticalSection. :stupid:

MyRealName 31. Mär 2015 18:18

AW: Thread freigeben, wenn er festklebt :)
 
festkleben heisst, dass eine Stored Proc nie wieder kommt und UniDAC für immer wartet. Das blockiert den Thread.

aCriticalSection ist eine globale variable im MainThread, welche beim erstellen des Services erzeugt wird und auch wieder freigegeben wird.
Und die Option des Abschiessens ist nicht... ähm... optimal, aber eine stored proc in dem Kontext sollte nie länger als 1-3 Sekunden dauern, wenn es länger dauert, dann ging was unerwartetes schief und ich muss das mit einbedenken.

Dejan Vu 31. Mär 2015 19:29

AW: Thread freigeben, wenn er festklebt :)
 
Wie kann denn eine SP nie zurückkommen? Die Frage sollte mit 'NIE' beantwortet werden.

MyRealName 31. Mär 2015 20:43

AW: Thread freigeben, wenn er festklebt :)
 
Eine option ist zum Bsp. die WAIT Transaction, die nicht einen lock conflict herbeiführt, sondern wartet bis die andere ihre updates abgschlossen hat. Hatte Falle wo niemand mit einer DB verbunden war und ich die SP nie zurück kam in der WAIT transaction. eine NO_WAIT brachte sofort einen lock conflict, obwohl niemand dort war. Ein Neustart von Firebird brachte die Lösung.

UniDAC hat keine Option, "normal" zu beenden mit TimeOut oder so. UniDAC blockiert immer den ganzen Thread.

Dejan Vu 1. Apr 2015 06:38

AW: Thread freigeben, wenn er festklebt :)
 
Mein Beitrag ging in die Richtung: Dann würde ich diese Implementierung überdenken bzw. dafür sorgen, das eine Query/SP nie hängt. Das sollte mit FB doch zu machen sein.

"Abschießen" o.ä. ist jedenfalls die unsauberste aller Implementierungsoptionen.

Alternativ kann man sich asynchron verbinden und die Verbindung bei Zeitüberschreitung beenden. Ob das mit UniDAC geht, weiß ich nicht.

Edit: Ich verstehe zwar nicht genau, was der TE mit 'DB' meint, und das ein Thread sich DBs teilt, aber grundsätzlich würde ich das nicht so lösen, sondern über einen Connectionpool. Pro Anfrage ein Thread, die sich aus dem Pool bedienen. Das löst das Problem zwar nicht, dürfte aber um vieles einfacher sein.

MyRealName 1. Apr 2015 13:22

AW: Thread freigeben, wenn er festklebt :)
 
Es ist ein Connection Pool, aber ich mache nicht pro Anfrage ein thread, weil das viel zu lange dauert. Manchmal habe ich nach einer Kostenneuberechnung im System ein paar tausend Dokumente neu zu buchen in der Buchhaltung (deren Bewegung vom Inventarkonto zum Ausgabenkonto). Pro Dokument kann das in total schonmal 1-2 Sekunden dauern. Wenn ich für jede von diesen Aufgaben enen neuen Thread machen wurde, wäre der Overhead an Zeit einfach zu gross. Ich kriege ja schon Mecker vom Chef, weil ich die nicht im Block abarbeite, sondern ene transaktion für jede mache und mir in Sammelthreads sogar noch zeit für andere DBs nehme ;)

Klar, die beste Lösung ist wenn die SP nie hängt, aber das schafft man auch nur zu 99%. Da kommen viel Faktoren mit rein. Versuche nur, an alles zu denken und auch die 1% abzudecken :)
Unsere DB hat knapp 400 stored procs und ein paar hundert Trigger. In Kombination mit den Daten kann es da zu seltsamen zusammenspielen kommen, die man nicht im vorraus sieht.

Dejan Vu 1. Apr 2015 16:20

AW: Thread freigeben, wenn er festklebt :)
 
Oh.. Also 'as is'. Na ja. Dann muss man es eben so machen.

Wie wäre es mit Workerthreads, die eine Jobliste abarbeiten? Einen Workerthreadpool gibts hier im Forum.

Falls es untergegangen sein sollte: Imho sollte nicht der Thread abgeschossen werden (geht nicht, nur schlafenlegen geht), sondern die Connection. Und das sollte FB drauf haben. Oder Du lässt dir dir PID von der Connection geben (oder wie auch immer das bei FB heißt) und killst Du dann (wenn das bei FB geht).


Alle Zeitangaben in WEZ +1. Es ist jetzt 19:00 Uhr.
Seite 1 von 3  1 23      

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