Klassen unterbrechbar machen, die eigentlich nichts davon wissen (sollen?)
-
manni66 schrieb:
Da Klassen niemals ausgeführt werden, kann man sie auch nicht unterbrechen.
Schon klar, das meinte ich ja

manni66 schrieb:
Dein Lexer muss also darauf vorbereitet sein, er muss aber nicht wissen, ob er in einem separaten Thread herausgeführt wird.
Ja, aber wie?
Ich muss ja irgendwo in
get_nextdann entsprechenthread_should_stopaufrufen - welches eine member Funktion meiner worker Klasse ist. Um auf dieses Zugriff zu haben müsste der lexer aber eine Referenz auf diese worker Klasse speichern. Also würde er "wissen" dass er in einem eigenen thread ausgeführt wird.
-
Ist dir schon mal aufgefallen, dass man das Wort "Unterbrecher" nicht ohne "Erbrecher" schreiben kann?
Zum Thema:
Ich würde dem Lexer ein eigenes Flag "lexer_should_stop" geben. Das kannst du dann auf false setzen, wenn der Worker beendet werden soll. Dann weiß der Lexer nichts darüber, dass er in einem eigenen Thread läuft oder was ein Worker ist, bietet aber trotzdem die Möglichkeit, über irgend einen Mechanismus beendet zu werden. Um so ein Flag kommst du ja nicht rum - der Lexer muss ja wissen, dass er die Ausführung beenden soll.
-
Unterbrecher schrieb:
Ist dir schon mal aufgefallen, dass man das Wort "Unterbrecher" nicht ohne "Erbrecher" schreiben kann?
Ja. Und?
Unterbrecher schrieb:
Zum Thema:
Ich würde dem Lexer ein eigenes Flag "lexer_should_stop" geben. Das kannst du dann auf false setzen, wenn der Worker beendet werden soll. Dann weiß der Lexer nichts darüber, dass er in einem eigenen Thread läuft oder was ein Worker ist, bietet aber trotzdem die Möglichkeit, über irgend einen Mechanismus beendet zu werden. Um so ein Flag kommst du ja nicht rum - der Lexer muss ja wissen, dass er die Ausführung beenden soll.
Wie soll das mit einem flag im lexer gehen?? Der lexer wird doch nicht von außerhalb geprüft ob gestoppt werden soll, sondern er muss das selbst überprüfen. Das relevante flag befindet sich schließlich in der worker Klasse.
-
happystudent schrieb:
Ich muss ja irgendwo in
get_nextdann entsprechenthread_should_stopaufrufen - welches eine member Funktion meiner worker Klasse ist. Um auf dieses Zugriff zu haben müsste der lexer aber eine Referenz auf diese worker Klasse speichern. Also würde er "wissen" dass er in einem eigenen thread ausgeführt wird.Wenn du bei jeder Antwort flugs ein paar neue Bedingungen nachlegst, wirst du nicht weit kommen.
class lexer { ... std::atomic<bool> abort_function = false; token get_next() { if(abort_function) // hier beenden } };Wer auch immer entscheidet, dass der Thread zu beenden ist, muss abort_function auf true setzen.
-
Wenn dein Lexer so langsam ist, hast du andere Probleme...
-
manni66 schrieb:
Wer auch immer entscheidet, dass der Thread zu beenden ist, muss abort_function auf true setzen.
Hm ja, das meinte ich. Also ich komme wohl nicht darum rum das in irgendeiner Form direkt im lexer zu implementieren... Na gut, dann muss ichs wohl so machen^^
Kellerautomat schrieb:
Wenn dein Lexer so langsam ist, hast du andere Probleme...
Wie kannst du das beurteilen ohne überhaupt den input zu kennen? Ab einer bestimmten Größe geht jeder lexer mal in die Knie

Die Implementierung ist ziemlich genau nach dem oben verlinkten tutorial gemacht, dachte eigentlich da das von dieser Seite hier kommt ist die auch ziemlich gut...
-
happystudent schrieb:
Unterbrecher schrieb:
Ist dir schon mal aufgefallen, dass man das Wort "Unterbrecher" nicht ohne "Erbrecher" schreiben kann?
Ja. Und?
Nichts weiter.
happystudent schrieb:
Wie soll das mit einem flag im lexer gehen?? Der lexer wird doch nicht von außerhalb geprüft ob gestoppt werden soll, sondern er muss das selbst überprüfen. Das relevante flag befindet sich schließlich in der worker Klasse.
Du setzt das Flag von außerhalb über eine Methode des Lexers und dieser prüft intern das Flag. Woher soll er sonst wissen, dass er beendet werden soll?
-
Ich würde das Beschaffen des Inputs erst einmal aus dem Lexer herausziehen.
enum class input_status { success, end, cancelled }; struct input_element { input_status status; char value_if_success; }; struct input { virtual ~input(); virtual input_element get_next() = 0; }; struct lexer { explicit lexer(input &in); };Der hässliche Multi-Threading-Teil kommt in eine separate Klasse. Um mit dem Atomic nicht zu viel Zeit zu verschwenden, habe ich einen Optimierungsversuch unternommen. Ob das auf deiner Hardware unter deinem Workload relevant ist, weiß ich natürlich nicht.
struct cancellable_input : input { explicit cancellable_input(boost::iterator_range<char const *> remaining) : remaining(remaining) { } virtual input_element get_next() override { size_t const cancellation_latency = 1000; if (remaining.size() % cancellation_latency == 0) { //Es könnte unter Umständen sehr teuer sein ständig atomar zu lesen. //Deswegen wird das hier im Beispiel nur jeweils nach ${cancellation_latency} Elementen gemacht. if (cancelled) { return input_element{input_status::cancelled, 0}; } } if (remaining.empty()) { return input_element{input_status::end, 0}; } char c = remaining.front(); remaining.pop_front(); return input_element{input_status::success, c}; } void cancel() { cancelled = true; } private: boost::iterator_range<char const *> remaining; std::atomic<bool> cancelled; };
-
Ok, danke für den Vorschlag, werd ich mir gleich mal genauer anschauen

-
@TyRoXx
Das ist aber auch keine allgemeine Lösung.
Was wenn der Lexer kein Lexer ist, und auch mal pro Input Element mehrere Sekunden brauchen kann?
Oder wenn man sinnvollerweise nicht eine virtuelle Funktion pro Zeichen aufrufen mag.----
Die hier gestellte Frage ist mit C++ mMn. grundsätzlich unlösbar. Wenn man Funktionen unterbrechbar machen möchte, dann muss man sie halt unterbrechbar machen. Automagisch geht da nix.
Die einfachste Variante das zu machen ist wohl einatomic<bool> cancelFlag.
-
Vielleicht ist das so besser:
struct lexer { token get_next(std::atomic<bool> const &abort_function) { while ... { if(abort_function) // hier beenden } } };Die Referenz kann man beliebig tief weitergeben und so oft prüfen, wie nötig.
hustbaer schrieb:
Oder wenn man sinnvollerweise nicht eine virtuelle Funktion pro Zeichen aufrufen mag.
Weil virtuelle Methoden ja so teuer sind und es keine Templates gibt.
-
TyRoXx schrieb:
hustbaer schrieb:
Oder wenn man sinnvollerweise nicht eine virtuelle Funktion pro Zeichen aufrufen mag.
Weil virtuelle Methoden ja so teuer sind und es keine Templates gibt.
Ja, Templates zu verwenden wäre eine Möglichkeit. Du hast es halt mit virtuellen Funktionen gezeigt. Und ja, bei einem Lexer pro
chareine virtuelle Funktion aufrufen ist teuer. Kannst es ja mal ausprobieren wenn du meinst dass der Unterschied nicht signifikant wäre.
-
Ich würde mich auch an die Eingabe hängen.
Und dazu es nicht per if-if-if machen, sondern im Falle des Treadabbruchwunsches eine Exception werfen.
-
Ok, werd ich dann so machen. Eigentlich muss man ja nur ein Interface nach außen hin anbieten mit dem man abbrechen kann, hab da wohl zu kompliziert gedacht am Anfang
