Discussion:
Thread sofort abbrechen
(zu alt für eine Antwort)
Borsdorf, Thomas
2008-04-30 20:11:30 UTC
Permalink
Hi NG!

Ich hab mich, mal wieder, mit Threads auseinandersetzen müssen. Und
sofort hab ich eine Frage.

Folgendes Problem:
Ich habe in meiner Anwendung (die bestimmt mal zum Service wird...)
mehrere Threads, die Aufgaben triggern. Diese Aufgaben sollen zu
bestimmten Zeiten laufen, also z.B. alle 15min., jede Stunde, einmal am
Tag etc. Mein Ansatz, inspiriert von einem Ex-Kollegen der das mal so in
C++ realisiert hat, ist der das ich im Thread.Execute die Wartezeit bis
zum Ausführen berechne und den Thread dann schlafen schicke (mit sleep).
Nach dem sleep folgt die Verarbeitung, danach wird die Zeit bis zum
nächsten Ausführen neu berechnet etc. Das Ganze funktioniert wunderbar.
Auch wenn die Wartezeit einige Minuten oder auch Stunden ist klappt es.

Jetzt will ich die Anwendung (bzw. den Service) aber auch mal beenden.
Ein Thread.Terminate bringt hier nicht viel, da der Thread ja im sleep
hängt. Er wartet also bis der sleep rum ist, vielleicht also bis in ein
paar Stunden, dann wird er beendet. Aber das will ich natürlich nicht,
die Anwendung soll ja SOFORT beendet werden, spätestens nach wenigen
Sekunden.
Also hab ich TerminateThread ausprobiert. Das funktioniert natürlich,
nur ist das bestimmt nicht Mittel der Wahl, wobei es beim Beenden der
Anwendung vielleicht nicht so dramatisch ist.

Und jetzt die Frage:
Ist das wirklich so harmlos wie ich mir das vorstelle?
Ist der Ansatz grundsätzlich okay oder ein absolutes No-Go?
Welcher Ansatz wäre besser?
Hat jemand sowas schonmal realisiert? Auf welchem Weg?

MfG Thomas.
Heiko Nocon
2008-04-30 21:15:00 UTC
Permalink
Post by Borsdorf, Thomas
Ist das wirklich so harmlos wie ich mir das vorstelle?
Nein. Spätestens wenn im Thread systemglobale Resourcen belegt werden,
wird es kritisch.
Post by Borsdorf, Thomas
Ist der Ansatz grundsätzlich okay oder ein absolutes No-Go?
Extreme dirty. Keinesfalls empfehlenswert.
Post by Borsdorf, Thomas
Welcher Ansatz wäre besser?
Der Thread wartet. Und zwar auf mehrere Ereignisse gleichzeitig. Er
wacht auf, wenn _irgendeines_ davon auftritt. In deinem Fall würde es
sogar reichen, nur auf ein Cancel-Event des Haupthreads zu warten, denn
man kann der Wartefunktion sagen, wie lange sie maximal warten soll. Die
Hauptaufgabe wird also sozusagen zum Nebeneffekt.
Post by Borsdorf, Thomas
Hat jemand sowas schonmal realisiert? Auf welchem Weg?
S.o.

Das ist noch eine der einfachsten Geschichten, die beim Multithreading
anfallen. Es wird deutlich komplexer, sobald gemeinsame Resourcen
zwischen den Threads geshared werden müssen.
--
Wer Komponenten ohne Quelltext oder richtig miese Komponenten
oder gute Komponenten mit Quelltext, ohne die Source zu verstehen, sich verschafft,
um sie in Form "eigener" Programme in Verkehr zu bringen,
der wird mit Gefängnis nicht unter 5 Jahren bestraft.
Borsdorf, Thomas
2008-05-01 07:06:45 UTC
Permalink
Hi Heiko!
Post by Heiko Nocon
Post by Borsdorf, Thomas
Ist das wirklich so harmlos wie ich mir das vorstelle?
Nein. Spätestens wenn im Thread systemglobale Resourcen belegt werden,
wird es kritisch.
IdR werden externe Anwendungen bzw. Batch-Files gestartet werden, aber
es kann auch mal ein DB-Check dabei sein.
Ich hatte mir schon sowas gedacht. Aber da das Problem ja nur beim
Beenden der Anwendung auftritt...
Post by Heiko Nocon
Der Thread wartet. Und zwar auf mehrere Ereignisse gleichzeitig.
Naja, warten tut er eigentlich nicht. Es hängt ja im sleep fest. Könnte
ich ihn auch für x ms suspenden?
Post by Heiko Nocon
denn man kann der Wartefunktion sagen, wie lange sie maximal warten soll.
Also sowas wie "repeat sleep(1000) if pruefenaechsteausfuehrung then
machwas until terminated"? Daran hatte ich auch schon gedacht. Dann
würde das Warten auf Abbruch max. 1s dauern, das ist okay.
Post by Heiko Nocon
Es wird deutlich komplexer, sobald gemeinsame Resourcen
zwischen den Threads geshared werden müssen.
Außer ner Log-Datei hab ich momentan nix gesharetes, aber wie schon
geschrieben kann es passieren das FB-Operationen dazukommen, dann werd
ich damit wohl konfrontiert werden. Stichwort Critical Section, richtig?

MfG Thomas.
Matthias Hanft
2008-05-01 08:11:27 UTC
Permalink
Post by Borsdorf, Thomas
Also sowas wie "repeat sleep(1000) if pruefenaechsteausfuehrung then
machwas until terminated"? Daran hatte ich auch schon gedacht. Dann
würde das Warten auf Abbruch max. 1s dauern, das ist okay.
Ja, so mache ich das auch meistens. Wenn ich mal ganz programmiergeil
bin, erzeuge ich stattdessen irgendwelche Windows-Objekte, die man
"signalisieren" kann, und verwende dann im Thread "WaitForMultiple-
Objects" - dann läuft der weiter, wenn (mindestens) ein Objekt, auf
das er wartet, den Zustand "signalisiert" erhält (was z.B. von außen
zum Beenden gesendet worden sein kann).

Allerdings habe ich sowas mehr beim Warten auf serielle Schnittstellen
und andere Eingabegeräte verwendet - inwieweit man das mit Timern
kombinieren kann, weiß ich nicht.
Post by Borsdorf, Thomas
Außer ner Log-Datei hab ich momentan nix gesharetes, aber wie schon
geschrieben kann es passieren das FB-Operationen dazukommen, dann werd
ich damit wohl konfrontiert werden. Stichwort Critical Section, richtig?
AFAIK nicht - es genügt, wenn jeder Thread eine eigene Connection ver-
wendet.

Gruß Matthias.
Christien Müller
2008-05-01 08:43:34 UTC
Permalink
IdR werden externe Anwendungen bzw. Batch-Files gestartet werden, aber es
kann auch mal ein DB-Check dabei sein.
Sowas mach ich gern mit dem SQL-Server-Agent vom SQL-Server. Da ist alles
dabei was Du willst, inclusive Log.

ansonnsten gibt es diverse Scheduler bei www.codeproject.com und warum nicht
den Windows-Scheduler verwenden? Beispiele gibt es auch bei codeproject.

aber empfehlen würde ich den SQL-Server-Agent
Heiko Nocon
2008-05-01 16:06:54 UTC
Permalink
Post by Borsdorf, Thomas
Post by Heiko Nocon
Der Thread wartet. Und zwar auf mehrere Ereignisse gleichzeitig.
Naja, warten tut er eigentlich nicht. Es hängt ja im sleep fest. Könnte
ich ihn auch für x ms suspenden?
Du hast mein Posting nicht verstanden. Das war keine Beschreibung deines
Ansatzes, sondern die Kurzbeschreibung der korrekten Lösung. Die habe
ich dann in der Folge dargestellt, mitsamt einer Vereinfachung, die sich
für den speziellen Fall ergibt.

Also, im Prinzip geht das so:

var
Thread: TMyThread;
ThreadTerminationEvent: TEvent;

Haupthread startet Arbeitsthread mit:

begin
ThreadTerminationEvent:=TEvent.Create(nil,false,false,'');
Thread:=TMyThread.Create(ThreadTerminationEvent.Handle);
end;

und der Arbeitsthread sieht so aus:

Type
TMyThread=class(TThread)
private
FTermHandle: THandle;
protected
procedure Execute; override;
public
constructor Create(TermHandle: THandle); reintroduce;
end;

implementation

constructor TMyThread.Create(TermHandle: THandle);
begin
inherited Create(true);
FTermHandle:=TermHandle;
FreeOnTerminate:=true;
Resume;
end;

procedure Execute;
begin
while not Terminated and
WaitForSingleObject(FTermHandle,<Zeitspanne in ms>)=WAIT_TIMEOUT do
<Was auch immer>;
end;

Dann läuft also der Thread und führt alle <Zeitspanne in ms> <Was auch
immer> aus. Falls <Was auch immer> deutlich länger dauert als die
erwünschte Reaktionszeit auf das Abbruchsignal, muß darin wie üblich
regelmäßig die Terminated-Eigenschaft abgefragt werden.

will der Haupthread nun den Arbeitsthread beenden, macht er dies so:
begin
Thread.Terminate;
ThreadTerminationEvent.SetEvent;
ThreadTerminationEvent.Free;
end;

Das macht erstmal das, was du wolltest, ist aber immer noch nicht 100%ig
threadsafe.

Knobelaufgabe für zukünftige Multithreading-Programmierer: wo kann es
noch knallen und unter welchen Umständen? Zusatzaufgabe: Wie macht man
dieses letzte Loch auch noch dicht?

Tipp: Das Event ist nicht das Problem. ;o)
--
Wer Komponenten ohne Quelltext oder richtig miese Komponenten
oder gute Komponenten mit Quelltext, ohne die Source zu verstehen, sich verschafft,
um sie in Form "eigener" Programme in Verkehr zu bringen,
der wird mit Gefängnis nicht unter 5 Jahren bestraft.
Michael Winter
2008-05-01 20:13:45 UTC
Permalink
Hallo,
Post by Heiko Nocon
begin
Thread.Terminate;
ThreadTerminationEvent.SetEvent;
ThreadTerminationEvent.Free;
end;
Das sieht aber nicht gut aus. Ich habe jetzt kein Delphi hier, aber
macht das Zerstören des Delphi-Objekts nicht auch das Event selber
ungültig? Der Thread terminiert zwar in deinem Code wohl auch, wenn das
Windows-Event weg ist, aber schön ist das nicht.

-Michael
Heiko Nocon
2008-05-01 21:32:44 UTC
Permalink
Post by Michael Winter
Das sieht aber nicht gut aus. Ich habe jetzt kein Delphi hier, aber
macht das Zerstören des Delphi-Objekts nicht auch das Event selber
ungültig?
Ja, natürlich. Aber das Verhalten der Windows-Synchronisationsobjekte
ist zum Glück wohldokumentiert und threadsafe by design.

In diesem Fall bedeutet das, daß sowohl bei einem gerade aktiven
Wartevorgang als auch bei einem Versuch, auf das Handle zu warten,
nachdem es zuvor bereits geschlossen wurde, einfach nur die
Wartefunktion mit WAIT_FAILED als Ergebnis zurückkehrt. Über
GetLastError könnte der Thread dann sogar noch herausbekommen, was von
beidem passiert ist, falls das für irgendeinen Zweck nötig sein sollte.
--
Wer Komponenten ohne Quelltext oder richtig miese Komponenten
oder gute Komponenten mit Quelltext, ohne die Source zu verstehen, sich verschafft,
um sie in Form "eigener" Programme in Verkehr zu bringen,
der wird mit Gefängnis nicht unter 5 Jahren bestraft.
Michael Winter
2008-05-02 06:55:19 UTC
Permalink
Post by Heiko Nocon
In diesem Fall bedeutet das, daß sowohl bei einem gerade aktiven
Wartevorgang als auch bei einem Versuch, auf das Handle zu warten,
nachdem es zuvor bereits geschlossen wurde, einfach nur die
Wartefunktion mit WAIT_FAILED als Ergebnis zurückkehrt.
Im ersten Fall bin ich einverstanden. Im zweiten Fall kann es passieren,
dass ein anschließend neu erzeugtes Objekt die gleiche Handle-Nummer
bekommt.

-Michael
Heiko Nocon
2008-05-02 16:22:35 UTC
Permalink
Post by Michael Winter
Im ersten Fall bin ich einverstanden. Im zweiten Fall kann es passieren,
dass ein anschließend neu erzeugtes Objekt die gleiche Handle-Nummer
bekommt.
Stimmt, eine gewisse Wahrscheinlichkeit dafür besteht. Sehr viel
geringer ist allerdings die Wahrscheinlichkeit, daß diese Situation dann
auch noch genau in den paar dutzend Takten eintritt, die für den Thread
zwischen der Abfrage der Terminated-Eigenschaft und dem Beginn des
Wartens liegen, nur dann hätte sie eine negative Auswirkung.

Trotzdem: Es ist eine reale race condition. Sei die Wahrscheinlichkeit
auch noch so gering, sie macht meine Behauptung unhaltbar, daß die
Eventgeschichte in diesem Lehrbeispiel sicher wäre. Ist sie nicht.

Naja, wenigstens ein didaktisches Ziel des Lehrbeispiels habe ich
wirklich erreicht: Eine eindrucksvolle Darstellung, wie man es nicht
machen sollte. Allerdings durchaus etwas anders, als ich es ursprünglich
geplant hatte...

Wäre dann aber bitte auch noch jemand so nett, den absichtlich
eingebauten Fehler zu outen? Mir ist irgendwie gerade die Lust
vergangen, das Kaninchen aus dem Hut zu ziehen.
--
Wer Komponenten ohne Quelltext oder richtig miese Komponenten
oder gute Komponenten mit Quelltext, ohne die Source zu verstehen, sich verschafft,
um sie in Form "eigener" Programme in Verkehr zu bringen,
der wird mit Gefängnis nicht unter 5 Jahren bestraft.
Michael Winter
2008-05-02 18:49:28 UTC
Permalink
Post by Heiko Nocon
Wäre dann aber bitte auch noch jemand so nett, den absichtlich
eingebauten Fehler zu outen?
Peinlich. Ich komme nicht drauf. Abgesehen von dem Syntaxfehler
natürlich (and bindet stärker als =, IIRC).

Oder willst du darauf hinaus, dass der Thread aus anderem Grund bereits
beendet sein könnte und wegen FreeOnTerminate auf ein nicht mehr
existierendes Objekt zugegriffen wird? Nein, das glaube ich dann doch nicht.

-Michael, auf Klärung wartend
Heiko Nocon
2008-05-02 21:39:37 UTC
Permalink
Post by Michael Winter
Oder willst du darauf hinaus, dass der Thread aus anderem Grund bereits
beendet sein könnte und wegen FreeOnTerminate auf ein nicht mehr
existierendes Objekt zugegriffen wird?
Genau das war's.

Blöd? Ja, vielleicht. Dann ist es aber erstaunlich, wie oft man in real
existierenden Programmen auf exakt dieses Konstrukt stößt.
--
Wer Komponenten ohne Quelltext oder richtig miese Komponenten
oder gute Komponenten mit Quelltext, ohne die Source zu verstehen, sich verschafft,
um sie in Form "eigener" Programme in Verkehr zu bringen,
der wird mit Gefängnis nicht unter 5 Jahren bestraft.
Marian Aldenhövel
2008-05-02 08:22:12 UTC
Permalink
Hallo,
Post by Michael Winter
Das sieht aber nicht gut aus.
Ich stutzte auch zuerst, ist aber in dem Beispiel wohl ganz
OK so.
Post by Michael Winter
macht das Zerstören des Delphi-Objekts nicht auch das Event selber
ungültig?
Doch.

Aber signalisiert ist es, und Windows garantiert, daß die
Threads direkt aus ihren WaitFor* erlöst werden bevor das
SetEvent() zurückkehrt.

Wenn danach also niemand mehr ThreadTerminationEvent oder sein
Handle verwendet ist alles gut.
Post by Michael Winter
aber schön ist das nicht.
Es hat einen gewissen Hä?-Effekt. In echtem Code würde ich auch
das Free weiter nach hinten verschieben um den zu vermeiden.

Ciao, MM
--
Marian Aldenhövel, Rosenhain 23, 53123 Bonn
http://www.marian-aldenhoevel.de
"Success is the happy feeling you get between the time you
do something and the time you tell a woman what you did."
Soeren Muehlbauer
2008-05-03 07:57:25 UTC
Permalink
Hi,
Post by Heiko Nocon
procedure Execute;
begin
while not Terminated and
WaitForSingleObject(FTermHandle,<Zeitspanne in ms>)=WAIT_TIMEOUT do
<Was auch immer>;
end;
Das kompiliert so schon mal gar nicht (auch wenn die Platzhalter
sinnvoll gefüllt wären).
Post by Heiko Nocon
Dann läuft also der Thread und führt alle <Zeitspanne in ms> <Was auch
immer> aus. Falls <Was auch immer> deutlich länger dauert als die
erwünschte Reaktionszeit auf das Abbruchsignal, muß darin wie üblich
regelmäßig die Terminated-Eigenschaft abgefragt werden.
begin
Thread.Terminate;
ThreadTerminationEvent.SetEvent;
ThreadTerminationEvent.Free;
end;
Wie Du schon aufgelöst hast, kann es bei FreeOnTerminate Probleme geben.
Das nächste Problem taucht auf, wenn der Thread nicht FreeOnTerminate
gesetzt hat und mittels WaitFor (oder Free, welches auch WaitFor
auffruft) in einem Finalisierungsabschnitt einer Unit auf das Ende
gewartet wird. In Dlls sind beim Entladen (hier werden die Units
finalisiert) alle anderen Threads angehalten und lassen sich auch nicht
gut fortsetzen. Das führt dann meist zu einem Deadlock, da auf einen
Thread gewartet wird, welcher nie zum Ende kommen kann. Sowas sehe ich
auch alle Nase lang mal in irgendwelchen Komponenten. Der Schreiber
scheint sich dieser Problematik nicht bewusst zu sein.

Im allgemeinen ist IMHO Multithreading keine allzu schwierige
Geschichte, wenn man sich an einige Grundregeln hält.
Post by Heiko Nocon
Knobelaufgabe für zukünftige Multithreading-Programmierer: wo kann es
noch knallen und unter welchen Umständen? Zusatzaufgabe: Wie macht man
dieses letzte Loch auch noch dicht?
Kein FreeOnTerminate verwenden oder die Instanz nach dem Starten für
immer vergessen.

Sören

Ulf Marsche
2008-04-30 21:23:37 UTC
Permalink
Post by Borsdorf, Thomas
Ist das wirklich so harmlos wie ich mir das vorstelle?
Ist der Ansatz grundsätzlich okay oder ein absolutes No-Go?
Welcher Ansatz wäre besser?
Hat jemand sowas schonmal realisiert? Auf welchem Weg?
ich sehe den sinn nicht hier threads zu verwenden.
Warum löst du das nicht über stinknormale Timer?, die lassen sich
jederzeit ohne Probleme beenden.

lg ulf
Borsdorf, Thomas
2008-05-01 06:56:57 UTC
Permalink
Hi Ulf!
Post by Ulf Marsche
Warum löst du das nicht über stinknormale Timer?, die lassen sich
jederzeit ohne Probleme beenden.
Soviel ich weis gibt es unter Windows nur eine begrenzte Anzahl Timer
die zur Verfügung stehen. Und mein Ansatz erschien mir einfacher zu
realisieren als das Ganze mit nur einem Timer, der dann nur jeweils bis
zur nächsten Aufgabe wartet. Denn dabei muss ich ja nach jeder Aufgabe
prüfen welche die Nächste ist um den Timer einzustellen.

Klar, ist auch ein Ansatz, den ich überdenken werde.
Post by Ulf Marsche
lg ulf
MfG Thomas.
Holger Lembke
2008-05-01 08:10:21 UTC
Permalink
Post by Borsdorf, Thomas
Soviel ich weis gibt es unter Windows nur eine begrenzte Anzahl Timer
die zur Verfügung stehen.
Die Grenze dürfte deutlich über 2000 Stück liegen.
--
mit freundlichen Grüßen! Password Must Be at Least 18770 Characters
Holgi, +49-531-3497854 ! Can't Repeat Any of Your Previous 30689 Passwords
Marian Aldenhövel
2008-05-01 09:16:41 UTC
Permalink
Hallo,
Post by Borsdorf, Thomas
Soviel ich weis gibt es unter Windows nur eine begrenzte Anzahl Timer
die zur Verfügung stehen.
Einer reicht ja. Dann prüfst Du eben einmal pro Sekunde oder Minute welcher
Job "fällig" ist. Also ganz gemütliches Polling.

Etwas ähnliches geht natürlich auch mit den Threads. Statt in einem Schritt
bis zur nächsten geplanten Aufgabe zu schlafen schickst Du den Thread in den
Sekundenschlaf:

while not Terminated do
begin
When:=ScheduleNextJob();
while (not Terminated) and (Now<=When) do
Sleep(1000);
if not Terminated then DoJob();
end;

Die CPU-Belastung ist immer noch gering. Nicht optimal, aber normalerweise
absolut akzeptabel.

Ciao, MM
--
Marian Aldenhövel, Rosenhain 23, 53123 Bonn
http://www.marian-aldenhoevel.de
"Success is the happy feeling you get between the time you
do something and the time you tell a woman what you did."
Ulf Marsche
2008-04-30 21:24:23 UTC
Permalink
Post by Borsdorf, Thomas
Ist das wirklich so harmlos wie ich mir das vorstelle?
Ist der Ansatz grundsätzlich okay oder ein absolutes No-Go?
Welcher Ansatz wäre besser?
Hat jemand sowas schonmal realisiert? Auf welchem Weg?
ich sehe den sinn nicht hier threads zu verwenden.
Warum löst du das nicht über stinknormale Timer?, die lassen sich
jederzeit ohne Probleme beenden.

lg ulf
Marian Aldenhövel
2008-05-01 09:11:49 UTC
Permalink
Hallo,
Post by Borsdorf, Thomas
Ist das wirklich so harmlos wie ich mir das vorstelle?
Nein. Du hast ja keine Garantie dafür, daß Du den Thread nur im Schlaf
killst. Er könnte gerade was wichtiges tun, bei dem man ihn besser
nicht mittendrin erschießt.
Post by Borsdorf, Thomas
Ist der Ansatz grundsätzlich okay oder ein absolutes No-Go?
Da es nicht schwer ist, ein besseres System zu ersinnen, würde ich
zu letzterem tendieren.
Post by Borsdorf, Thomas
Welcher Ansatz wäre besser?
Statt Sleep lässt Du den Thread auf ein Event warten. Mit Timeout.
Siehe WaitForSingleObject.

Wenn der Thread aus dem WaitForSingleObject wieder zurückkommt
ist entweder das Ereignis signalisiert worden (WAIT_OBJECT_0) und
er kann sich selbst sauber beenden. Oder der Timeout ist abgelaufen
(WAIT_TIMEOUT), dann tut er einmal seine Arbeit, und ruft wieder
WaitForSingleObject auf.

Das Event signalisiert Deine Anwendung nun wenn sie sich beenden
will. Dann wartet sie ihrerseits höflich auf das saubere Ende aller
gestarteten Threads. Die, die gerade beschäftigt sind könnten etwas
länger dazu brauchen weil sie ihren aktuellen Job noch beenden
werden. Wenn das ein Problem ist musst Du den Job noch zerhacken und
"unterwegs" auf das Event prüfen.

Dann beendet sich die Anwendung ganz normal.

Ciao, MM
--
Marian Aldenhövel, Rosenhain 23, 53123 Bonn
http://www.marian-aldenhoevel.de
"Success is the happy feeling you get between the time you
do something and the time you tell a woman what you did."
Lesen Sie weiter auf narkive:
Suchergebnisse für 'Thread sofort abbrechen' (Fragen und Antworten)
5
Antworten
Pille und Antibiotikum?
gestartet 2009-06-02 06:31:04 UTC
frauen
Loading...