Synchrones Abbrechen mit oder ohne sleep?`



  • Hallo,

    ich will eine Klasse schreiben, die eine relativ teure Operation ausführt. Diese Operation soll nach belieben abbrechbar sein, und zwar sowohl asynchron als auch synchron.

    Dafür habe ich mir folgende Methode überlegt:

    #include <iostream>
    #include <atomic>
    #include <thread>
    #include <chrono>
    
    class foo
    {
    	std::atomic<bool> running, cancel_requested;
    public:
    	foo() : running(false), cancel_requested(false) {}
    	foo(foo const &other) : cancel_requested(other.cancel_requested.load()), running(other.running.load()) {}
    	foo &operator=(foo const &other) { cancel_requested = other.cancel_requested.load(); running = other.running.load(); return *this; }
    	foo &operator=(foo &&other) { cancel_requested = std::move(other.cancel_requested.load()); running = std::move(other.running.load()); return *this; }
    
    	void do_something()
    	{
    		running = true;
    		for (int i = 0; i < 100; ++i) // Mach irgendwas was lange dauert
    		{
    			std::this_thread::sleep_for(std::chrono::milliseconds(10));
    			std::cout << "At step " << i << '\n';
    			if (cancel_requested)
    			{
    				std::cout << "cancelled!\n";
    				break;
    			}
    		}
    		running = false;
    	}
    
    	void cancel_async() { cancel_requested = true; }
    	void cancel() { cancel_async(); while (running) { /*Sollte man hier sleep-en oder nicht?*/ } }
    
    	bool is_running() const { return running; }
    };
    
    int main()
    {
    	foo f;
    	std::thread t(&foo::do_something, &f);
    	while (true)
    	{
    		std::this_thread::sleep_for(std::chrono::milliseconds(100));
    		if (f.is_running())
    		{
    			f.cancel_async();
    		}
    	}
    }
    

    Jetzt drei Fragen: Ist die Methode so in Ordnung? Und: Sollte man in der while-Schleife der cancel Methode einen sleep (von vielleicht einer Millisekunde) einbauen, oder nicht? Und passt das mit dem bool running so, oder kann es da irgendwelche Probleme geben?



  • Dir ist klar, dass an deinem Code absolut nichts asyncron ist?

    Das passiert alles in ein und dem selben Thread und damit kann f.cancel() auch gar nicht funktionieren, denn f.do_something(); kommt erst zurueck, wenn es beendet ist.

    Selbst im async scenario macht this_thread::sleep_for in der working funktion ueberhaupt keinen Sinn.

    Wenn cancel_requested ein atomic ist muss running auch ein atomic sein.
    Das willst du ja wahrscheinlich in einem Thread setzen und in dem anderen lesen.

    Bau mal in deinem Codebeispiel wirklich multithreading ein und dann kann man das ganze besser beurteilen.



  • Also die fehlende Asynchronität wurde ja schon erwähnt. Ich gehe dann mal direkt zur Frage über.

    Also sleep ist in der Regel die falsche Lösung. Aber ohne sleep ist es in Deinem Fall noch weniger richtig. Das ist ein busy wait. Die richtige Lösung ist eine Condition. Der Thread, der den anderen abbrechen will, der wartet auf eine Condition. Der Thread, der abgebrochen wird, signalisiert die Condition. Ab besten solltest Du das Signalisieren in einen Destruktor einer Klasse einbauen, die in der Thread-Methode auf dem Stack angelegt wird. Und das setzen der running Flags genauso. Dadurch wird das auch ausgeführt, wenn Deine Thread-Methode mit einer Exception oder vorzeitigen return aussteigt.



  • Hallo,

    sorry, hatte aus dem Original-Code zuviel rausgekürzt. Hab den Code entsprechend upgedated (ist jetzt asynchron und zeigt ein Beispiel).

    tntnet schrieb:

    Also sleep ist in der Regel die falsche Lösung. Aber ohne sleep ist es in Deinem Fall noch weniger richtig. Das ist ein busy wait. Die richtige Lösung ist eine Condition. Der Thread, der den anderen abbrechen will, der wartet auf eine Condition. Der Thread, der abgebrochen wird, signalisiert die Condition. Ab besten solltest Du das Signalisieren in einen Destruktor einer Klasse einbauen, die in der Thread-Methode auf dem Stack angelegt wird. Und das setzen der running Flags genauso. Dadurch wird das auch ausgeführt, wenn Deine Thread-Methode mit einer Exception oder vorzeitigen return aussteigt.

    Ok, aber was genau ist mit signalisieren jetzt gemeint? Ist das einfach wieder einfach eine atomic<bool> Variable oder wie meinst du das?



  • happystudent schrieb:

    Ok, aber was genau ist mit signalisieren jetzt gemeint? Ist das einfach wieder einfach eine atomic<bool> Variable oder wie meinst du das?

    Schau Dir an, was Conditions sind. Dann wirst Du das verstehen, was Signalisieren bedeutet. Und wenn Du da Verständnisprobleme hast, dann fragst Du hier. Wir helfen Dir gerne.


Anmelden zum Antworten