Interrupt threads



  • Hallo @all ,

    ich habe folgendes Problem bzw. folgende Frage:

    Um einen Thread zu beenden habe ich bisher immer eine Pollingvariante benutzt:

    std::atomic<bool> abbrechen;
    
    void doThread()
    {
        while( !abbrechen)
        {
            // Do Stuff
        }
        return;
    }
    

    Nun ist diese Variante leider sehr langsam, da a: zyklisches Abfragen Ressourcen frist und b: atomics irgendwie ziemlich langsam sind :S

    Meine Frage ist nun: Gibt es ne Möglichkeit obiges Problem mit einem Interruptzuschreiben?
    Ich stell mir das so vor: die Threadfunktion läuft und wenn der User z.B. auf nen Abbrechen-Button drückt, schickt das Programm einen Interrupt, welcher vom Thread erkannt und behandelt wird.

    Ich könnte zwar sowas wie KillThread o.Ä. nutzen, aber ich möchte, dass meine Ressourcen (z.B. ein Puffer ) ordentlich freigegeben werden und nicht irgndwo als Speicherleck herumschwirren.

    mfG

    shft



  • Condition Variables?



  • @shft
    In welcher Grössenordnung bewegt sich "Do Stuff"?
    Hast du Sleep() Aufrufe drinnen um das System nicht auszubremsen?
    Bzw. hast du etwa gar eine "busy wait" Schleise die weder nen Sleep noch nen Wait auf ne Condition-Variable verwendet wenn's nix zu tun gibt?

    Wenns immer was für den Thread zu tun gibt, er nicht Sleept und "Do Stuff" wenigstens ein paar tausend CPU Zyklen braucht, dann würde ich empfehlen gar nix zu ändern, weil deine Lösung bereits optimal ist.
    Falls "Do Stuff" zu kurz ist, dann ändere die Schleife so dass bloss alle 10 oder 100x "stuffen" die Atomic-Variable geprüft wird.
    Wenn du Sleep()st dann nimm statt dem Sleep() nen Wait auf ne Condition-Variable.
    Und wenn du busy-waiting machst dann geh dich schämen.

    shft schrieb:

    Ich stell mir das so vor: die Threadfunktion läuft und wenn der User z.B. auf nen Abbrechen-Button drückt, schickt das Programm einen Interrupt, welcher vom Thread erkannt und behandelt wird.

    So ähnliche Dinge gibt's schon. Sind aber alle mehr oder weniger problematisch. Daher macht man das auch nicht so.



  • Der Algorithmus liest in jedem Durchlauf einige Zeichen aus einer Datei,
    verarbeitet diese und schreibt in eine Zieldatei; Bei großen Dateien hab ich also mehrere 100 Schleifendurchläufe,
    wo durch der Speedup mit einem Counter ( also nur jeden 100. o.Ä. Durchlauf abfragen ) enorm ist.

    Ich dachte bzgl. Interrupts gäbe es vllt eine einfache tolle Methode, aber anscheinend reicht die Abfragen zu verringern.



  • sjft schrieb:

    Der Algorithmus liest in jedem Durchlauf einige Zeichen aus einer Datei,
    verarbeitet diese und schreibt in eine Zieldatei; Bei großen Dateien hab ich also mehrere 100 Schleifendurchläufe,
    wo durch der Speedup mit einem Counter ( also nur jeden 100. o.Ä. Durchlauf abfragen ) enorm ist.

    Das fällt mir schwer zu glauben.
    Atomics Lesen ist lange nicht so langsam dass das einmalige Lesen einer Atomic-Variable pro Schleifendurchlauf in einer Schleife die File-IO macht merkbar bremsen könnte.



  • Hm...
    Ich habe aber mit verringertem Lesen eine Laufzeit von 11 Sekunden und ohne 17 Sekunden... vllt liegt das daran, dass ich in meinem Algorithmus 2 Variablen habe ( eine wird gelesen und eine geschrieben ) , dass Schreiben dauert natürlich wesentlich länger...



  • shft schrieb:

    Hm...
    Ich habe aber mit verringertem Lesen eine Laufzeit von 11 Sekunden und ohne 17 Sekunden...

    Entspricht auch meiner Messung, wenn die Schleife praktisch gar gar nix tut.

    #include <iostream>
    #include <atomic>
    using namespace std;
    
    #define SLOW
    
    int main() {
    	double x=0.2;
    	atomic<int> poison;
    	poison.store(0);
    	for(int i=0;i<1000000000;++i){
    		x=x*x-2;
    #ifdef SLOW
    		if(poison)
    			break;
    #endif
    	}
    	cout<<x<<'\n';
    }
    

    8.547 zu 4.019
    Und

    #include <iostream>
    #include <atomic>
    using namespace std;
    
    int main() {
    	double x=0.2;
    	atomic<int> poison;
    	unsigned char c=0;
    	poison.store(0);
    	for(int i=0;i<1000000000;++i){
    		x=x*x-2;
    		if(!++c && poison){
    			break;
    		}
    	}
    	cout<<x<<'\n';
    }
    

    4.029



  • Ja, wenn die Schleife *nix* tut.
    Was sjft beschreibt ist aber nicht nix, sondern etwas.
    Und OK, "merklich bremsen" darf es da auch - 10% oder so sind auch "merklich".
    Aber es darf nicht so 1:2 reinhauen.

    Allerdings egal, die Abfragen auf alle N Durchläufe zu beschränken hat ja keine echten Nachteile, also muss man der Sache auch nicht unbedingt nachgehen.



  • Hab Riesenmist gebaut, sorry.
    Die Messung war im Debug-Modus und ist damit total hinfällig.

    Im Releasemodus kann ich keinen Unterschied messen: Bei 50 Starts hab ich 0.1% Unterschied, in einer Schleife, die fast nix tut.
    Es riecht nach Nullkosten. 😮

    //Hab damit gemessen
    #include <iostream>
    #include <atomic>
    #include <signal.h>
    using namespace std;
    
    atomic<int> poison;
    
    void my_handler(int param){
    	poison.store(1);
    }
    
    int main() {
    	poison.store(0);
    	signal(SIGINT,my_handler);
    	try{
    		double x=0.2;
    		for(uint32_t i=0;i<4000000000;++i){
    			x=x*x-2;
    			if(poison)
    				throw 1;
    		}
    		cout<<x<<'\n';
        }
        catch(...){
    		cout<<"terminated gracefully!\n";
        }
    }
    

    -O3 -march=native -pthread

    Das Schreiben in einen atomic kostet aber. Habs probiert mit Threads, die einen atomic<int> mit ++ hochzählen:
    Mit volatile int:
    1 Thread 1.56s
    2 Threads komplett falsches Ergebnis
    Mit atomic<int>: (4EchteKerne+4Hyperthreading)
    1 Thread 3.34s
    2 Threads 12.6s
    3 Threads 12.3s
    4 Threads 12.3s
    5 Threads 12.4s
    6 Threads 12.3s
    7 Threads 12.4s
    8 Threads 12.6s
    9 Threads 12.6s
    10 Threads 12.6s
    20 Threads 12.7s
    50 Threads 12.7s
    100 Threads 12.7s

    shft schrieb:

    Hm...
    Ich habe aber mit verringertem Lesen eine Laufzeit von 11 Sekunden und ohne 17 Sekunden... vllt liegt das daran, dass ich in meinem Algorithmus 2 Variablen habe ( eine wird gelesen und eine geschrieben ) , dass Schreiben dauert natürlich wesentlich länger...

    Ja, vielleicht liegt's sogar *nur* am Schreiben.


Log in to reply