[gelöst] Blockierenden Thread sauber (ohne Memory Leak) abbrechen

Für Fragen von Einsteigern und Programmieranfängern...
Helios
Beiträge: 67
Registriert: Mi 29. Jun 2011, 22:36
OS, Lazarus, FPC: Lazarus 2.0.12 Windows 10 64Bit / Lazarus 2.0.10 Debian 11 „Bullseye" 64Bit
CPU-Target: 64Bit
Wohnort: Leonberg

[gelöst] Blockierenden Thread sauber (ohne Memory Leak) abbrechen

Beitrag von Helios »

Hallo Forianer,

nach langer Zeit der Recherche ohne konkreten Erfolg, nun die Frage an Euch:
Ich möchte einen blockierenden Thread sauber (ohne Memory Leak) abbrechen.
Ein ähnliches Thema hatte ich bereits,
viewtopic.php?f=17&t=13687&start=15
aber dabei konnte ich das Hauptproblem umgehen. Es gibt auch in diesem Forum und im Web schon ähnlich beschriebene Probleme, aber eine konkrete
Lösung habe ich dabei nicht finden können.
Nun habe ich den Fall, dass ich einen blockierenden Aufruf (das kann eine SQL Abfrage aber auch eine Datenverbindung oder, wie im Beispielfall eine Endlosschleife sein)
nicht vermeiden kann, und ich bekomme den Thread einfach nicht sauber bedendet (auch nicht über eine Exception wie im genannten und gelösten Fall einer Firebird SQL Abfrage). Da es sich eigentlich um ein "Grundproblem" handelt, das sicher schon jemand gelöst hat, meine Frage an euch. Ich habe mich schon durch viele (Web) Seiten von den Beispielprogrammen im Lazarus Example Verzeichnis, bis hin zu Event-, Critical-Sections, diverse Terminate Eigenschaften durchgekämpft aber ich sehe wohl den Wald vor lauter Bäumen nicht mehr.
Im Anhang ist ein Beispielprojekt,
BlockingThread.zip
(126.49 KiB) 5-mal heruntergeladen
welches das Grundproblem simuliert (abgeleitet aus den Lazarus Thread Examples). Das Problem ist die Endlosschleife (while true do ; ) die den/einen blockierenden Aufruf simuliert.
Der Thread hängt als Zombie nach dem Thread.Terminate im System herum und ich bekomme ihn nicht (sauber) abgeschlossen/gekillt/beendet.
Hat jemand eine Idee wie es richtig gemacht wird?
Danke für jeden Hinweis
Euer Helios
Zuletzt geändert von Helios am Di 12. Okt 2021, 18:48, insgesamt 1-mal geändert.

Benutzeravatar
theo
Beiträge: 8671
Registriert: Mo 11. Sep 2006, 19:01

Re: Blockierenden Thread sauber (ohne Memory Leak) abbrechen

Beitrag von theo »

Vielleicht verstehe ich dich nicht richtig, aber warum nimmst du statt "while true do" nicht "while (not Terminated) do" ?

Warf
Beiträge: 1584
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: MacOS | Win 10 | Linux
CPU-Target: x86_64
Wohnort: Aachen

Re: Blockierenden Thread sauber (ohne Memory Leak) abbrechen

Beitrag von Warf »

Das ist kein einfaches problem. Wenn du den Thread terminierst dann wird kein weiterer code mehr ausgeführt, auch keine finally blöcke. Das bedeutet das alle allokationen die bis dahin gemacht wurden und auf free warten nie gefreed werden.

Beispiel:

Code: Alles auswählen

Test := TStringList.Create;
try
  Sleep(1000000);
finally
  Test.Free;
end;
Wenn du während dem sleep den thread killst dann wird das finally nie ausgeführt und Test nie gefreed.

Das kann man verschieden lösen, aber keine der Lösungen sind tatsächlich gut.
1. Hab nen watchdog thread der überwacht:

Code: Alles auswählen

// Thread1:
Test := TStringList.Create;
try
  Sleep(1000000);
finally
  FreeAndNil(Test);
end;
// Thread 2 (watchdog):
while not Thread1.Terminated do
  sleep(100);
// Thread1 wurde terminiert
if Assigned(Thread1.Test) then // aber ohne das Test gefreed wurde
  FreeAndNil(Thread1.Test); // hol das nach
Option 2: benutz einen thread der nicht blocken kann und mach da die allokationen und nicht auf dem thread der blocken kann:

Code: Alles auswählen

// Nicht blockender thread
Test := TStringList.Create;
try
  BlockingThread := TBlockingThread.Create(Test);
  BlockingTHread.Start;
  while not terminated and not BlockingThread.Terminated do
    Sleep(100);
  if Terminated then
    // Stoppe blocking thread
    pthread_cancel(BlockingThread.ThreadID);
finally
  Test.Free;
end;

// blockierender thread
Sleep(10000000);
Option 3: benutz signale (oder exceptions) zum aufräumen

Code: Alles auswählen

procedure KillSignalHandler(sig: Integer); cdecl;
begin
  Thread1.Test.Free;
  pthread_cancel(Thread1.ThreadID);
end;

// Thread1:
Test := TStringList.Create;
try
  Sleep(1000000);
finally
  FreeAndNil(Free);
end;
Und dann statt direkt zu killen, sende ein signal das den signal handler called welcher dann erst freed und dann killt

kupferstecher
Beiträge: 310
Registriert: Do 17. Nov 2016, 11:52

Re: Blockierenden Thread sauber (ohne Memory Leak) abbrechen

Beitrag von kupferstecher »

Warf hat geschrieben:
Mo 11. Okt 2021, 19:29
Wenn du den Thread terminierst dann wird kein weiterer code mehr ausgeführt, auch keine finally blöcke.
In seinem Beispielcode schießt er den Thread doch nicht ab, sondern beendet ihn regulär mittels "MyThread.Terminate;".

@TE, ein Thread läuft immer so lange, bis die Funktion 'Execute' verlassen wird. Beim Aufruf von MyThread.Terminate wird die Variable Terminate (eigentlich fTerminate) auf true gesetzt, mehr passiert nicht. In der Schleife in Execute liest du diese regelmäßig aus, ist sie 'true', solltest du die Execute-Funktion zeitnah verlassen.

Also wie theo schon sagte, einfach
while not Terminted do [...]
verwenden.
Oder alternativ in der Schleife die Variable 'Terminated' regelmäßig abfragen, das ist bspw. in for-Schleifen sinnvoll.
if Terminated then BREAK;

Helios
Beiträge: 67
Registriert: Mi 29. Jun 2011, 22:36
OS, Lazarus, FPC: Lazarus 2.0.12 Windows 10 64Bit / Lazarus 2.0.10 Debian 11 „Bullseye" 64Bit
CPU-Target: 64Bit
Wohnort: Leonberg

Re: Blockierenden Thread sauber (ohne Memory Leak) abbrechen

Beitrag von Helios »

Hallo Theo und Kupferstecher,
kurz nur zur Erklärung: Die Endlosschleife "while true do" soll einen blockierenden Aufruf simulieren und die macht das Thema so kompliziert.
Programmcode, der in der Schleife "while (not Terminated) do" mit entsprechender Geschwindikkeit ausgeführt wird ist generell kein Problem, da
ist die Welt in Ordnung (z.B. wenn man die "while true do" Schleife auskommentiert) weil die Schleife mehrfach durchlaufen und dabei immer wieder
auf Terminated geprüft wird. Leider ist die Welt in diesem (und einigen anderen anwendungsrelevanten Fällen) nicht so einfach.
Warf hat mich da verstanden.

Hallo Warf
ich Danke Dir für die Rückmeldung. Wenn ich die richtig vestehe (z.B. Option 2) sollte ich "meinen" Blockierenden Thread in
2 Threads aufteilen. Einen der die relevanten Speicher Aufrufe über *.Create erzeugt und einen weiteren Thread der nur die blockierende Anweisung
(ohne dynamisch allokierte Variablen/Objekte) enthält.
Hmm, der "pthread_cancel" wird unter Windows 10 (64Bit) wahrscheinlich nicht nutzbar sein (sorry darauf hatte ich nicht explizit hingewiesen),
sondern nur unter Posix bzw. Linux.
Option 3 verstehe ich leider nicht, da ist der Beispielcode für mich zu kurz geraten. Wo rufe ich den KillSignalHandler auf. Ganz normal in meinem Main Thread?
Du erwähnst, dass keine der Lösungen richtig gut sei. Wo ist denn Deiner Meinung nach der Haken/das Risiko?

Danke nochmal. Den neuen Impulsen gehe ich gerne nach und melde mich wieder.
Gruß Helios

Warf
Beiträge: 1584
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: MacOS | Win 10 | Linux
CPU-Target: x86_64
Wohnort: Aachen

Re: Blockierenden Thread sauber (ohne Memory Leak) abbrechen

Beitrag von Warf »

Nun ich weiß nicht gneau wie man das ganze in windows threads benutzt und auch nicht ob es nicht cross plattform möglichkeiten in der RTL oder FCL oder so gibt. pthread_cancel ist die pthread (posix) funktion um einen thread zu killen ohne wenn und aber.

Zu Option 3, die Idee hierbei ist thread signale zu verwenden. Ein signal ist so zu sagen die software version eines interrupts. Ein programm aber auch ein thread kann einen signal handler registrieren. Von außen (von einem anderen programm oder thread) kann dann ein signal an diesen thread gesendet werden. Darauf hin hält der thread sofort an und führt den registrierten signal handler aus. Wenn der signal handler fertig ist wird da weiter gemacht wo der thread pausiert wurde.

Das kannst du benutzen um den thread zu killen. Du registrierst KillSignalHandler für ein signal (z.b. SIGUSR1 ist frei verfügbar). Wenn du dann den thread killen willst sendest du das Signal an den thread, was den thread anhält und die funktion ausführt. Da der thread angehalten wird, kommt es zu keinen bösen überraschungen wenn du versuchst gleichzeitig den speicher zu freen während der thread weiter macht. Der Signal handler kann dann den speicher aufräumen, während der thread noch am schlafen ist, und killt ihn danach.

Mit pthreads müsste das irgendwie so aussehen

Code: Alles auswählen

// Am anfang des programms registrier den signal handler
fpsignal(SIGUSR1, @KillSignalHandler); // registriert den signal handler allgemein, also für alle threads

// der signal handler
procedure KillSignalHandler(Signal: Integer); cdecl;
begin
  // Aufräumen
  MyThread.Cleanup;
  // Thread killen
  pthread_cancel(MyThread.ThreadID);
end;

// Um den thread zu killen dann einfach signal an den thread senden
pthread_kill(MyThread.ThreadID, SIGUSR1);
Was ich aber bei meinem vorigen post vergessen habe, das geht natürlich sehr einfach für manuelles memory management wie mit Klassen. Strings oder Arrays hingegen werden referenzgezählt. Da ist das eine ganz schöne hackerei wenn man die auch freen will. Vor allem jedwede internen kopien von irgendwelchen funktionen über die du keine kontrolle hast sind natürlich dann nicht mehr in deiner kontrolle.
Auch hier gibt es eine sehr gehackte Lösung, du könntest einen Memor Manager schreiben der Memory pro thread separat verwaltet und dann einfach in einem mass free am ende alle speicherbereiche wegräumst die nach dem killen noch über sind.

Aber damit programmierst du dich dumm und dämlich. Die Frage ist daher eher ob es nicht eine möglichkeit gibt diese blockenden calles nicht blockend oder mit timeout oder so zu benutzen/bauen.
Alternativ könntest du natürlich auch statt threads einen prozess starten. Wenn du einen prozess abschießt kümmert sich das OS ums aufräumen. Dann wird allerdings die kommunikation zwischen dem prozess und deinem hauptprogramm schwerer als es bei einem thread ist.

Wie gesagt, es gibt Lösungen für dein Problem, aber schön finde ich wirklich keine davon. Das ist tatsächlich ein Problem über das ich auch schon gestolpert bin, was mich auch schon sehr gestört hat. Und in den meisten Fällen hab ich allerdings dann die Lösung im ändern des Blockenden calls gefunden (also z.b. bei Netzwerk Kommunikation kann man mit Select einen Timeout reinmachen)

Helios
Beiträge: 67
Registriert: Mi 29. Jun 2011, 22:36
OS, Lazarus, FPC: Lazarus 2.0.12 Windows 10 64Bit / Lazarus 2.0.10 Debian 11 „Bullseye" 64Bit
CPU-Target: 64Bit
Wohnort: Leonberg

Re: Blockierenden Thread sauber (ohne Memory Leak) abbrechen

Beitrag von Helios »

Hallo Warf,
vielen Dank für die Antwort in der Nacht. Mit Deiner Option 2 bin ich ein Stück weiter (siehe Anhang).
Einen übermäßigen Speicherverbrauch kann ich nicht erkennen. Allerdings schraubt sich die Prozessorlast mit etwa 20% bei jedem Start/Stop hoch bis ich bei 100% am Poller bin.
Es sieht so aus, als ob der (blockierende) Thread gar nicht beendet wird. Dabei habe ich die üblichen Verdächtigen schon alle, teilweise in Kombination ausprobiert:
- MyBlockingThread.Terminate;
- MyBlockingThread := NIL;
- MyBlockingThread.CleanupInstance;
- killthread(MyBlockingThread.ThreadID)
- MyBlockingThread.Free;
Was noch fehlt ist
- pthread_cancel(MyBlockingThread.ThreadID);
unter Linux. Das probiere ich noch aus. Hast Du eine Idee warum der (Blockierende) Thread nicht gestoppt werden kann? Ist das ein Free Pascal Bug?
Den blockierende Call in meinem Fall lässt sich nicht entschärfen (Closed Source). Das Thema über einen Prozess zu regeln hatte ich schon auf meiner Liste, aber wie Du
schon sagtest, ist die Datenübergabe dort nicht so schön wie bei einem Thread. Noch glaube ich an eine Lössung via Thread(s). 8)
Danke aber nochmal und Gruß
Helios
Dateianhänge
BlockingThread2.zip
(126.91 KiB) 1-mal heruntergeladen

Warf
Beiträge: 1584
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: MacOS | Win 10 | Linux
CPU-Target: x86_64
Wohnort: Aachen

Re: Blockierenden Thread sauber (ohne Memory Leak) abbrechen

Beitrag von Warf »

Unter Windows kannst du mal TerminateThread ausprobieren. Das dürfte das WinAPI äquivalent zu pthread_cancel sein.
- MyBlockingThread.Terminate;
- MyBlockingThread := NIL;
- MyBlockingThread.CleanupInstance;
- killthread(MyBlockingThread.ThreadID)
- MyBlockingThread.Free;
Ich weiß nicht genau was killthread macht, aber alle anderen sachen aus der liste enforcen keinen kill

Helios
Beiträge: 67
Registriert: Mi 29. Jun 2011, 22:36
OS, Lazarus, FPC: Lazarus 2.0.12 Windows 10 64Bit / Lazarus 2.0.10 Debian 11 „Bullseye" 64Bit
CPU-Target: 64Bit
Wohnort: Leonberg

Re: Blockierenden Thread sauber (ohne Memory Leak) abbrechen

Beitrag von Helios »

Hallo Warf,

leider habe ich weder unter Linux (Debian) mit
- pthread_cancel(MyBlockingThread.ThreadID)
noch mit
- TerminateThread
Erfolg gehabt, beide Versionen verhalten sich gleich. Die Prozessorlast steigt kontinuierlich mit jedem Start /Stop.
Vielleicht habe ich in meinem Code irgendwas falsch deklariert bzw. falsch initialisiert.

TerminateThread habe ich umgesetzt mit

Code: Alles auswählen

uses ..., ..., Windows

function TerminateAThread(hThread: DWORD): Boolean;
var ec: DWORD;
begin
  Result := False;
  if not GetExitCodeThread(hThread, ec) then Exit;
  if not TerminateThread(hThread, ec) then Exit;
  Result := True;
end;  

TerminateAThread(MyBlockingThread.ThreadID);
Sonst alles wie beim Beispiel "BlockingThread2.zip".
Puh, ich brauche eine Mütze Schlaf. Vielleicht habe ich morgen noch eine Eingebung. Ansonsten freue ich mich über jeden Hinweis zur Lösung des Problems.
Danke Dir und Gruß
Helios

Warf
Beiträge: 1584
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: MacOS | Win 10 | Linux
CPU-Target: x86_64
Wohnort: Aachen

Re: Blockierenden Thread sauber (ohne Memory Leak) abbrechen

Beitrag von Warf »

In der Windows API musst du handle benutzen, nicht ThreadID.

Bezüglich des problems im allgemeinen, ich habe eine Lösung für POSIX gefunden die das ganze problem sogar recht elegant löst:

Code: Alles auswählen

program Project1;

{$mode objfpc}{$H+}

uses
  cthreads, SysUtils, Classes, pthreads, BaseUnix;

type

  { TTestThread }

  TTestThread = class(TThread)
  protected
    procedure Execute; override;
  end;

procedure KillThreadSigHandler(sig: Integer); cdecl;
var
  unblock: sigset_t;
begin
  // Wir sind jetzt in einem Signal Context, d.h. das aktuelle signal kann nicht noch einmal gefeuert werden
  // Wir haben allerdings nicht vor diesen Context je wieder zu verlassen
  // Deshalb müssen wir manuell das signal wieder aktivieren
  FpsigEmptySet(unblock);
  FpSigAddSet(unblock, sig);
  pthread_sigmask(SIG_UNBLOCK, @unblock, nil);
  // Das signal ist einfach auf dem stack oben drauf, wir können also exceptions
  // benutzen um wieder zurück zu springen
  raise Exception.Create('Exception');
end;

{ TTestThread }

procedure TTestThread.Execute;
begin
  try
    try
      WriteLn('Start');
      While True do;
    except on E: Exception do
      WriteLn(E.Message);
    end;
  finally
    WriteLn('End');
  end;
end;
      
var
  T: TTestThread;
begin
  FpSignal(SIGUSR1, @KillThreadSigHandler);
  T := TTestThread.Create(False);
  Sleep(100);
  pthread_kill(T.ThreadID, SIGUSR1);
  T.WaitFor;
  WriteLn('Exit'); 
end.
Erklärung:
Wenn wir das signal SIGUSR1 feuern, wird das in dem Threadcontext ausgeführt, wenn wir also darin eine Exception feuern, sieht es für das exception handling so aus als würde die exception von dem aktuell gefeuertem code kommen.
In diesem fall ist das also der "While True do;" endlosloop.
Die Exception sorgt dann dafür das dieser loop abgebrochen wird und statdessen der try-except block ausgeführt wird.
Zusätzlicher vorteil, das löst auch jedes try-finally aus und sorgt damit für ein sauberes aufräumen selbst in Code von unterfunktionen über die du eventuell keine kontrolle hast.

Wenn kein Try-Except block da ist wird der thread einfach terminiert, allerdings wird auch dann natürlich zu erst alle Try-Finally blöcke ausgeführt.
Zuletzt geändert von Warf am Di 12. Okt 2021, 03:46, insgesamt 1-mal geändert.

Warf
Beiträge: 1584
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: MacOS | Win 10 | Linux
CPU-Target: x86_64
Wohnort: Aachen

Re: Blockierenden Thread sauber (ohne Memory Leak) abbrechen

Beitrag von Warf »

Ich habe mir das ganze auch mal unter windows angesehen. Da ist es nicht ganz so leicht in einem Thread eine funktion einfach mal auszuführen.
Über einen kleinen hack habe ich aber auch da etwas gefunden:

Code: Alles auswählen

program test;

{$mode objfpc}{$H+}

uses
  SysUtils, Classes, Windows;

type

  { TTestThread }

  TTestThread = class(TThread)
  private
    procedure BlockingCall;
  protected
    procedure Execute; override;
  public
    procedure SendException;
  end;

{ TTestThread }

procedure TTestThread.BlockingCall;
begin
  While True do;
end;

procedure TTestThread.Execute;
begin
  try
    try
      WriteLN('Start');
      BlockingCall;
    except on E: Exception do
      WriteLN(E.Message);
    end;
  finally
    WriteLn('End');
  end;
end;

procedure TTestThread.SendException;
var
  ctx: PCONTEXT;
label
  JumpPos;
begin
  New(ctx);
  try
    SuspendThread(Handle);
    GetThreadContext(Handle, ctx);
    // Setze den Instruction Pointer auf unsere JumpPos
    // das sorgt dafür das sobald der thread fortgesetzt wird, er direkt die exception wirft
    ctx^.Rip := QWord(@JumpPos);
    SetThreadContext(Handle, ctx^);
    ResumeThread(Handle);
  finally
    Dispose(ctx);
  end;
  Exit;
JumpPos:
  raise Exception.Create('Exception');
end;
     
var
  T: TTestThread;
begin
  T := TTestThread.Create(False);
  Sleep(100);
  T.SendException;
  T.WaitFor;
  ReadLn;
end.
Das problem hierbei ist das unter Windows benutzt der FPC eine andere art von Exception Handling (SEH exceptions), und die mögen es gar nicht wenn man manuell am Kontrollfluss rumspielt. Das geht also in der Form so nicht.

Allerdings könnte das ganze funktionieren, wenn man die SEH exceptions beim FPC ausschaltet. Leider muss man dafür den FPC komplett neu Kompilieren, und es wird langsam spät, also belass ich es erst einmal dabei.

Bezüglich des Killen des Threads, TerminateThread mit dem Handle funktioniert wunderbar:

Code: Alles auswählen

var
  T: TTestThread;
begin
  T := TTestThread.Create(False);
  Sleep(100);
  TerminateThread(T.Handle, 0);
  T.WaitFor;
  ReadLn;
end.

Helios
Beiträge: 67
Registriert: Mi 29. Jun 2011, 22:36
OS, Lazarus, FPC: Lazarus 2.0.12 Windows 10 64Bit / Lazarus 2.0.10 Debian 11 „Bullseye" 64Bit
CPU-Target: 64Bit
Wohnort: Leonberg

Re: Blockierenden Thread sauber (ohne Memory Leak) abbrechen

Beitrag von Helios »

Hallo Warf,
die Lösung für Linux mit "TTestThread.SendException" ist der Hammer. Das reservierte "label" habe ich bisher auch noch nicht
gekannt bzw. nutzen müssen. Wo kann man so etwas nachlesen/lernen oder hast Du noch einen Ausbildungsplatz frei :D ?
Diese Exception gesteuerte Vorgehensweise ist das worauf ich gehofft hatte. So etwas unter Windows, ohne das eine darüber gelegte
GUI in Mitleidenschaft geraten kann, wäre dann die Lösung.
Das man so tief in das Betriebssystem hinabsteigen muss, ich habe es befürchtet.
Ich bin gespannt was Du da noch aus dem Hut zauberst. Ich bin hier leider schon programmiertechnisch "abgehängt". Ich lese, lerne und staune über Deine Vorschläge.
Danke Dir dafür
Gruß
Helios

Helios
Beiträge: 67
Registriert: Mi 29. Jun 2011, 22:36
OS, Lazarus, FPC: Lazarus 2.0.12 Windows 10 64Bit / Lazarus 2.0.10 Debian 11 „Bullseye" 64Bit
CPU-Target: 64Bit
Wohnort: Leonberg

Re: Blockierenden Thread sauber (ohne Memory Leak) abbrechen

Beitrag von Helios »

Hallo Warf,
mit dem TerminateThread hatte ich es in der Eile/Nacht unter Windows irgendwie falsch umgesetzt.
Mit Deinem
--> TerminateThread(T.Handle, 0);
funktioniert es nun richtig.
Somit gibt es eine Lösung unter Windows und unter Linux mit Deiner Hilfe. Damit wäre ich dann zufrieden.
Ich teste das nun konkret mit einem echten Blocking Call und melde dann das Ergebnis zurück.
Wenn es klappt, dann kann ich das Thema doch noch schließen... Bin gespannt...
Vielen Dank nochmal für Deine super Unterstützung!
Gruß
Helios

PS: Anbei ein entsprechend aktualisiertes Projekt zum Testen für geneigte Interessenten:
Dateianhänge
BlockingThread3.zip
(127.06 KiB) 9-mal heruntergeladen

Helios
Beiträge: 67
Registriert: Mi 29. Jun 2011, 22:36
OS, Lazarus, FPC: Lazarus 2.0.12 Windows 10 64Bit / Lazarus 2.0.10 Debian 11 „Bullseye" 64Bit
CPU-Target: 64Bit
Wohnort: Leonberg

Re: [gelöst] Blockierenden Thread sauber (ohne Memory Leak) abbrechen

Beitrag von Helios »

Hallo zusammen,
nun die Bestätigung:
Die Blockierende Anweisung (hier ein Query.Open auf einen Datenbankserver via ODBC) lässt sich abbrechen und
die SQL Abfrage lässt sich ohne Weiters wieder neu starten.
Die Client (GUI) Applikation mit der gethreadeten SQL-Abfrage kann mehrfach (bis 12 getestet) gestartet und abgebrochen werden. Speicherverbrauch und
Prozessorlast sind client-seitig unauffällig. Der DB Server scheint die ODBC Abfrage Abbrüche zu verkraften (das ist aber in jedem Einzelfall genau noch zu untersuchen).
Somit Thema hinreichend gelöst (bei Thread Applikationen ist das ja immer unter Vorbehalt bis genügend Testtiefe erreicht ist).
Großen Dank an Warf
Viele Grüße
Helios

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 4701
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Niederösterreich
Kontaktdaten:

Re: [gelöst] Blockierenden Thread sauber (ohne Memory Leak) abbrechen

Beitrag von af0815 »

Server können per design gut mit den Abbrüchen leben. Der von mir beobachte MS SQL Server hat einen entsprechenden Threadpool und abgebrochene Abfragen und nicht mehr bediente Verbindungen werden entsprechend freigegeben.

Da hat man am Client mehr Probleme. Wenn die IP Verbindung bricht mitten in der Abfrage ist es oft nicht möglich das sauber und zeitnah zu erkennen. Deswegen verstehe ich deine Versuche das im Thread zu lösen.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Antworten