Threadgestaltung bei erwünschtem Abbruch
-
Michael E. schrieb:
dot schrieb:
Ich muss gestehen ich bin mir nicht ganz sicher was du mit Interruption Points genau meinst
Das, was hier unter "Interruption" steht. Läuft im Prinzip auf ne Exception raus. Deshalb hab ich etwas übertrieben, als ich sagte, dass man sich an allen möglichen Stellen ums abort-Flag kümmern muss, weil man dieses Verhalten genauso mit einer Exception simulieren kann, die man in der Top-Level-Funktion des Threads fängt

Dacht ich mir. Das ist aber eben genau das was ich mich abort Flag meinte. Ob man da jetzt eine Exception wirft (was sicherlich sinnvoll ist) oder sonstwas tut ist dann ein Implementierungsdetail, es ändert nix daran dass man eben an allen möglichen Stellen checken muss ob der Thread aborted werden soll.

Ich denk dieser check lässt sich rein prinzipiell einfach nicht vermeiden.Michael E. schrieb:
Wie sähe deine Vorgehensweise beim Abbrechen mit einer Queue und Condition Variable eigentlich aus?
Na genau so. Die Queue und Condition Variable haben ja mit dem Abbrechen nix zu tun, sondern nur damit wie die Arbeit gestartet wird. Anstatt immer einen neuen Thread zu machen gibts eben einen Thread der in einer Schleife Arbeit von der Queue abholt.
-
dot schrieb:
Das ist aber eben genau das was ich mich abort Flag meinte.
Achso. Dann hab ich nichts gesagt.
Michael E. schrieb:
Wie sähe deine Vorgehensweise beim Abbrechen mit einer Queue und Condition Variable eigentlich aus?
Na genau so. Die Queue und Condition Variable haben ja mit dem Abbrechen nix zu tun, sondern nur damit wie die Arbeit gestartet wird. Anstatt immer einen neuen Thread zu machen gibts eben einen Thread der in einer Schleife Arbeit von der Queue abholt.
Ich meinte, wie du dem Worker Thread mitteilst, dass er seine aktuelle Berechnung abbrechen und sich die neuen Parameter anschauen soll.
-
Naja, ich würd wohl sowas machen:
class Abortable { private: volatile bool is_aborted: protected: void check_abort() const { if (is_aborted) throw abort_exception(); } public: void abort() { is_aborted = true; } }; class Calculation : public Abortable { public: void calc() { // do stuff check_abort(); // do stuff ... } };D.h. die Berechnung ist eben in einem Objekt gekapselt. Wenn eine neue Berechnung gestarted werden soll, dann wird eben so ein Objekt erzeugt, ein Zeiger drauf in die Queue gepushed und auf einer evtl. momentan laufenden Berechnung abort() aufgerufen.
Aber prinzipiell ist das ja nicht so wichtig. Worum es ja eigentlich hier geht war das Beenden und was das angeht sind wir uns ja einig dass man eben im Thread einen Check (Flag oder gleich die Library Lösung per boost interruption point) brauchen wird

-
Michael E. schrieb:
Ich meinte, wie du dem Worker Thread mitteilst, dass er seine aktuelle Berechnung abbrechen und sich die neuen Parameter anschauen soll.
Naja, Beispiel:
TaskHandle h = queue.QueueTask(work); h.Cancel();
OK, "work" muss das nun noch irgendwie mitbekommen. Wie man das implementiert, hängt nun davon ab, was QueueTask() als Parameter nimmt - also was "work" für nen Typ haben darf.
Wenn man super-generisch sein will, und QueueTask() nen std::function<void()> frisst, dann wird's ein wenig kompliziert. Dann muss man wohl oder übel mit Thread-Local-Storage arbeiten.Wenn man dagegen einfach verlangt, dass jeder Task ("work") eine Schnittstelle ITask implementiert, dann kann diese ja z.B. einfach so aussehen:
class ITask { public: // ... virtual void Run() = 0; virtual void Cancel() = 0; };Der Task wiederum kann das dann machen wie er möchte, ein atomic cancel-flag verwenden, bool + Mutex, was auch immer (oder den Cancel() Aufruf gleich ganz ignorieren).
-
Ah, ich dachte, da gibts dann irgend nen tollen Trick mit einer Condition Variable, den ich nicht kenne.
-
Achso nein, die CV war nur gedacht zum Schlafen auf der Queue

-
@dot: Warum volatile? Wolltest du nicht atomic<bool> sagen?
-
Für ein Cancel-Flag reicht volatile mMn. aus.
Es muss ja nicht sofort vom anderen Thread "gesehen" werden, und das Lesen/Schreiben muss normalerweise auch keine Memory-Order implizieren.
Zumindest so lange man die Information "canceled" nicht mit irgendwas anderes verknüpft.
Es reicht also, wenn wir sicherstellen, dass der Lese-/Schreibvorgang nicht komplett wegoptimiert wird. Und genau dafür ist volatile gut, wenn ich mich recht erinnere.Wenn der gecancelte Task natürlich im "if (Canceled())" irgendwas macht, was sich darauf verlässt dass bestimmte Dinge jetzt so-und-so sein müssen, weil "sonst wäre ich ja nicht gecanceled worden", dann wäre das natürlich böse.
Ein Programm dass solche Abhängigkeiten enthält, würde ich aber schon mal grundsätzlich als problematisch bezeichnen, d.h. die Restriction dass man sowas eben nicht machen darf, halte ich für nicht sehr schlimm.Und da ein volatile Read/Write auf vielen Plattformen um Grössenordnungen billiger ist als ein atomic Read/Write...
(Man vergebe mir das schlimme Denglisch in diesem Beitrag, aber es schert mich jetzt nicht das nochmal umzuschreiben *g*)
-
Nö ich meinte schon volatile, warum müsste der denn deiner Meinung nach atomic sein?
EDIT: Genau was hustbaer sagt

-
Warum habt ihr dann damals bei meinem (zugegebenermaßen hässlichen) IRC-Server so heftig protestiert?
-
Vermutlich weil volatile dort eben nicht ausreichend war? Manche Leute verwechseln volatile gern mit "threadsafe"...
-
Weiss ich nimmer, dazu müsstest du den Thread rauskramen

Idealerweise einfach nen Link posten, dann können wir dort weiterdiskutieren um diesen Thread nicht noch mehr zuzumüllen.
-
Den Thread find ich wohl nicht mehr. Ich weiß nur noch, dass mein Code in etwa so aussah:
volatile bool run = true; void do_server_stuff() { while(run) do_stuff; } void exit_server() { run = false; }
-
Das ist ok.
-
Naja, das alleine sieht harmlos aus.
Aber wie gesagt, ich kann mich nimmer erinnern.
-
Thread gefunden. www.c-plusplus.net/forum/p2104471#2104471
Überlest einfach mal, was da sonst so steht. Ist ja peinlich.
-
Auch das was in dem Thread sieht für mich OK aus. Tjoah... Threads sind halt so ein Thema was kaum wer wirklich versteht. Auch ich würde mein Leben nicht darauf verwetten dass alles so 100% stimmt wie ich es meine verstanden zu haben. Ich bin mir bloss seeeehr sicher, aber was heisst das schon

Es findet sich sicher auch jmd. der das was in diesem Thread geschrieben wurde für Unfug hält
-
314159265358979 schrieb:
Thread gefunden. www.c-plusplus.net/forum/p2104471#2104471
Überlest einfach mal, was da sonst so steht. Ist ja peinlich.
Die Diskussion zu volatile drehte sich nicht darum, dass volatile hier nicht geht, sondern darum, dass deine Auffassung, was volatile macht, falsch war, dass du dir dadurch Performancezuwachs erhofft hast, ohne wirklich zu messen, was andere (evtl. bessere) Lösungen in dem Fall kostet (pauschal "0,5ms" angeben ist bestenfalls Mutmaßung), und dadurch dein Server unnötig rödelt.
-
Lt Standard, ist volatile ein Hinweis (!) für den Compiler, agressive Optimierung zu vermeiden. Ansonsten soll es sich genau so verhalten, wie es im C-Standard definiert ist. Und dort steht:
- A volatile declaration may be used to describe an object corresponding to a memory-mapped
input/output port or an object accessed by an asynchronously interrupting function. Actions on
objects so declared shall not be ‘‘optimized out’’ by an implementation or reordered except as
permitted by the rules for evaluating expressions.
Ich verstehe das so, dass ein Zugriff auf ein mit volatile spezifiziertem Element nicht vor die vor im stehenden Zugriffe reordered werden darf, und dass das Element im Speicher vorhanden sein muss (es also nicht komplett wegoptimiert werden darf).
Damit ist es nicht atomar (was bei bool aber keinen Unterschied machen sollte).
Wann exakt andere Threads eine Änderung an einem volatile sehen ist damit nicht vorhersagbar, sondern hängt vom Rest des Programms, sowie der verwendeten Maschine ab.
Ist das so in etwas richtig?
- A volatile declaration may be used to describe an object corresponding to a memory-mapped
-
Hi,
danke für die zahlreichen Antworten! Ich habe mir alles durchgelesen und bin nun erfreut, dass mein Verständnis sich verbessert hat. Warum der Zähler über die Leitplanke springt, weiß ich immer noch nicht, denn die eigentliche Zeile ist:
for(std::size_t pos = 0; pos < 2598960; ++pos)
und er dumpt damit, dass die Arraygrenzen bei pos = 2598960 überschritten sind, aber das analysier ich lieber, wenn ich den Rest umgebaut hab.Threads sind halt so ein Thema was kaum wer wirklich versteht.
Oh, danke! Ich dachte, ich würde irgendwo hinterm Mond leben. Ich programmier ja mittlerweile auch ein kleines Weilchen und hatte nie mit Threads zu tun (bedauerlich, ja). Jetzt bastel ich bei dem UI-Zeug damit rum und merke, dass das ein ganz neues Thema ist. Fühle mich wie ein kleines Kind.

Auch mit atomic... Wert wird in Register geschrieben, inkrementiert und zurückgeschrieben... ich hätte jetzt einfach ein rohes bool genommen *schäm*
Edit: Ok, was ja volatile auch reicht. Aber ist diese Definition eigentlich erzwingend? Wenn das nur eine "Recommendation" für den Compiler ist, dann könnte er es ja auch ignorieren. Das jedoch wäre fatal.Irgendwie braucht man für MT echt das Wissen, wie jedes kleine Detail abläuft. Das braucht man sonst oft nicht.