Smartpointer für Flag-Übergabe?
-
Hallo,
ich habe ein Problem und merke dabei auch, dass ich wieder zu wenig von den Smartpointern verstehe. Ich habe eine Klasse Container. Über diese wird von Zeit zu Zeit ein Thread aufgerufen. Von Zeit zu Zeit möchte der Benutzer von Container gerne den Thread abbrechen können. Dafür erhält der Thread eine Referenz auf ein Flag, was über Container verwaltet wird.
Das Problem ist jetzt: wenn der Container stirbt, soll der Thread natürlich auch zerstört werden. Aber das Flag ist ja an die Lebenszeit des Containers gebunden, also wird der Thread, wenn der Container stirbt, das Flag prüfen wollen und es kommt zu einem Lesefehler, weil es ja schon vernichtet wurde.
Meine Idee: Ich wollte einen shared_ptr um das Flag herumwickeln. Der Thread erhält dann ebenfalls einen shared_ptr und wenn der Container vor dem Thread stirbt, ist das nicht so schlimm.
Das Problem ist jedoch: Container ist copyable. Und wenn der shared_ptr kopiert wird, dann wird der Wert invalide. Mein Flag (über shared_ptr erreichbar) ist z.B. die ganze Zeit über false, aber wenn ich den Container kopiere, dann wird das Flag automatisch true, was sich darin äußert, dass der Thread sofort terminiert (das Flag ist ja das terminate-Flag).
Na ja, jetzt weiß ich nicht, ob meine Idee mit dem shared_ptr überhaupt schlau ist. Das Kopieren ist sonst nicht so schlimm, weil sowieso immer nur ein Thread pro Objekt existiert. Aber wenn man davon absieht, ist der nicht dafür geeignet die Lebenszeit eben an das spätere Ende von zwei Objekten zu koppeln und daher genau das, was ich hier brauche?
Ich hoffe, man konnte was mit meiner Beschreibung anfangen. Pseudocode folgt notfalls, ist aber auch gar nicht Mal so unkomplex.
-
Eisflamme schrieb:
Das Problem ist jedoch: Container ist copyable. Und wenn der shared_ptr kopiert wird, dann wird der Wert invalide.
Welcher Wert? Und wieso?
-
Eisflamme schrieb:
Das Problem ist jedoch: Container ist copyable.
Wieso? Was passiert mit den verwalteten Threads bei einer Kopie?
-
Gibt´s pro Container nur einen Thread? Falls ja, macht es Sinn, den Thread als Member des Container zu verwalten? Falls ja kannst du den Thread im Destruktor der Container Klasse beenden.
-
camper:
Der von der Variable, die der Smartpointer kapselt. Ich habe gerade so was (schlagt mich bitte nicht zu hart, falls ihr das wollt):boost::shared_ptr<volatile bool> abortThread;initialisiert wird der im Elementinitialisierer der Klasse mit:
Container::Container(/* ... */) : /* ... */, abortThread(new bool(false))Nach Kopie von Container ist *abortThread oder *abortThread.get() (falls die Syntax jetzt nicht stimmt: ich meine halt einfach den Wert des bools) true statt false! Warum? Weiß ich nicht, führe ich gerade auf mein Unverständnis von shared_ptr zurück, ich dachte, den dürfte man kopieren.
Athar:
Es ist nicht so supersauber, aber Container kommt aus einem Pool. Container hat sozusagen zwei States active und inactive. Kopiert wird das Ding nur in der inactive-Variante. Threads werden lediglich in der active-Variante genutzt. Es können aber während der Laufzeit einesContainers mehrere Threads (hintereinander! zu einem Zeitpunkt ist Container : Thread immer 1 : 0/1) ablaufen. Nur der letzte ausgeführte Thread kann wie gesagt über die Laufzeit vonContainerhinauslaufen. Das ist zwar nicht gewünscht, aber der lässt sich ja nicht einfach abrupt stoppen, nur weil ich das gerne hätte, oder? Meistens garantiert ein Abort oder so das nicht, soweit ich das im Kopf hab?!DocShoe:
Genau das hätte ich ja gerne. Aber das Flag zum Aborten des Threads wird eben inContainerverwaltet und der Thread hält eine Referenz drauf und prüft das ständig. WennContainerstirbt, ist die Referenz nicht mehr gültig. Ich kann den Thread ja nicht so beenden, dass er mit Sicherheit keine Anweisung mehr ausführt, oder? Durch das Flag kriege ich ihn nach kurzer Zeit zum stoppen, aber u.U. dauert das zu lange und RRID hat das Flag, verwaltet von Container schon vernichtet.
-
Eisflamme schrieb:
Nach Kopie von Container ist *abortThread oder *abortThread.get() (falls die Syntax jetzt nicht stimmt: ich meine halt einfach den Wert des bools) true statt false! Warum?
Schuss ins Blaue: im Destruktor setzt du das Flag auf true? Dann musst du wissen, dass beim Kopieren des Shared-Pointers (was implizit beim Kopieren von Container passiert) das Objekt (hier der bool) geteilt wird, d.h. die Kopie und der ursprüngliche Container beziehen sich über unterschiedliche Shared-Pointer auf dasselbe bool-Objekt. Wenn nun die Kopie zerstört wird (etwa, weil es eine temporäre Kopie ist), wird also das Flag true gesetzt und bezieht sich dann auch auf den ursprünglichen Container.
Es gibt sicher bessere Möglichkeiten. Z.B. könnte ich mir vorstellen, dass es eine Besitzbeziehung zwischen Thread und Container gibt, womit du entweder den Thread zum Member des Containers machen kannst, oder andersherum den Container zum Member des Threads. Oder aber du arbeitest mit Callbacks, dass der Container dem Thread sein Zerstören mitteilt.
-
ipsec schrieb:
Schuss ins Blaue: im Destruktor setzt du das Flag auf true?
Dann könntest Du im Destruktor die Anzahl der shared_ptr auf Dein Flag abfragen und das Flag nur dann umschalten, wenn diese Anzahl kleiner als 3 ist. Denn bei 2 gibt es nur noch den in Deinem Thread und den in dem gerade zu zerstörenden Container.
-
Schuss ins Blaue
Wohl eher ins Schwarze! Vielen Dank für die Erklärung, ich wusste, irgendwie so was Offensichtliches habe ich übersehen. Meine Vorgehensweise lädt einfach zu Fehlern ein, mir fällt aber nichts Besseres ein. Und jetzt lese ich Deinen zweiten Absatz.
Es gibt sicher bessere Möglichkeiten. Z.B. könnte ich mir vorstellen, dass es eine Besitzbeziehung zwischen Thread und Container gibt, womit du entweder den Thread zum Member des Containers machen kannst, oder andersherum den Container zum Member des Threads. Oder aber du arbeitest mit Callbacks, dass der Container dem Thread sein Zerstören mitteilt.
Na ja, es gibt schon eine Besitzbeziehung, die eben besagt, dass - falls es gerade einen Thread gibt - der bitte sterben soll, wenn der Container stirbt. Blöd ist halt, dass "Thread" hier eigentlich ein asynchroner Funktionsaufruf ist. Denn ich dachte, es wäre einfacher das so zu gestalten als es über einen Thread laufen zu lassen... Und den asynchronen Aufruf kann ich ja nicht so einfach beenden.

Aber auch wenn es ein Thread wäre: Ich will den Thread ja über ein Flag steuern können (zusätzlich zur Besitzbeziehung!). Und das Flag ist eben im Container beheimatet (Thread hat Referenz drauf). Ich kann ja tun, was ich will, der Thread kann einfach irgendwann noch versuchen wollen auf das Flag zuzugreifen. (was er ja eigentlich auch soll, wenn der Container im dtor das Flag auf false setzt, um dem Thread mitzuteilen, dass er jetzt bitte Mal aufhören soll) Ist der Container aber schon tot, ist auch das Flag weg und ein Test vom Thread auf das Flag führt zum Dump. Dafür war mein shared_ptr ja gedacht!
-
Belli schrieb:
ipsec schrieb:
Schuss ins Blaue: im Destruktor setzt du das Flag auf true?
Dann könntest Du im Destruktor die Anzahl der shared_ptr auf Dein Flag abfragen und das Flag nur dann umschalten, wenn diese Anzahl kleiner als 3 ist. Denn bei 2 gibt es nur noch den in Deinem Thread und den in dem gerade zu zerstörenden Container.
Noch einfacher:
Im Destruktor machst Du gar nix. Im Thread machst Du statt einer Abfrage auf das Flag eine Abfrage auf die Anzahl der Referenzen, die noch auf Dein Flag existieren. Ist die kleiner als 2, dann ist der Thread der einzige, der noch einen shared_ptr auf Dein Flag hält, die Container sind alle tot ...
-
Stimmt, das ist tatsächlich einfacher!
Also ist die Abbruchprüfung ptr.use_count() < 2 || *ptr
Dankeschön!
-
Find´ das nicht schön...
Was ist denn, wenn du ein kleines Event System baust? Der Container bietet als Producer ein OnDestroy an, auf das sich der Thread als Subscriber anmeldet. Im Destruktor setzt der Container ein Event ab, der Thread bekommt das mit und beendet sich.
-
Von Zeit zu Zeit möchte der Benutzer von Container gerne den Thread abbrechen können.
Jaja, alles schon mal gelesen ... in der PPL.
Ich wollte einen shared_ptr um das Flag herumwickeln.
shared_ptr fuer ein Flag? Sinnfrei ...
Wenn ein Thread abgebrochen werden soll, wird einfach sowas wie thread.stop() gemacht. Ueber thread.is_running() kann der aktuelle Status abgefragt werden und gegebenenfalls aus deinem Thread-Pool entfernt werden.
-
Thread hier ist nicht up-to-date, weil ich es inzwischen anders gelöst habe.
shared_ptr fuer ein Flag? Sinnfrei ...
Wenn ich die schlechte Struktur von vorher unbedingt hätte beibehalten wollen, wäre das eben eine Lösung für das Problem der Lebenszeit gewesen. Weiß nicht, ob Dir das bewusst war.
Habe aber die Struktur verworfen und jetzt nen ThreadPool. Asynchrone Funktionsaufrufe kann man zwar mitnichten einfach stoppen, aber über das Flag kann ich die Aufrufe schnell beenden. Dann läuft der Aufruf aus und das Programm beendet sich automatisch danach, weil der dtor blockt. Klappt alles prima und ist jetzt auch sauber, wie ich finde
