- fchao-Sinus-Wechselrichter AliExpress         
Seite 1 von 2 12 LetzteLetzte
Ergebnis 1 bis 10 von 11

Thema: Frage zu pthread_testcancel();

  1. #1
    HaWe
    Gast

    Frage zu pthread_testcancel();

    Anzeige

    Praxistest und DIY Projekte
    hallo,
    ich bin noch dabei, für den Fall von Notstopp-Events meinen Robot sicher und schnell zum Stehen zu bringen und auch langdauernde Threads sicher und schnell abbrechen zu können.

    Beispielhaft folgendes vereinfachte Setup:

    main() initialisiert 2 pthreads, nämlich threadname1() und threadname2().

    einer davon startet, sobald er aufgerufen wird, eine rekursive Funktion, die sehr viel Rechenzeit erfordert (z.B. einen AStern).
    Code:
    void AStern(int x, int y, int xz, int yz) {
        // tutwas 
        //  ruft sich selber auf
         Astern(x+1, y+1, 100,90);  
    
       // tut wieder was bis zur Abbruchbedingung
    }
    
    void *threadname2 (void *arg) {        
    	printf("starte Astern\n");
            AStern(0,0, 100,90);        
    	return NULL;
    }
    es macht hier sicher nur wenig Sinn, pthread_testcancel() im pthread threadname2() auftzurufen, da ja nach Start sofort die Funktion AStern() angesprungen wird.
    Macht es aber Sinn, in der Funktion AStern() z.B. ganz am Anfang diesen Befehl zu setzen uns ggf. auch im Code zwischendurch auch nochmal, damit öfter (z.B. in jeder rekursiven Instanz) auf Abbruch von threadname2 getestet wird?

  2. #2
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    20.08.2008
    Ort
    Karlsruhe
    Alter
    36
    Beiträge
    1.225
    Zitat Zitat von HaWe Beitrag anzeigen
    Macht es aber Sinn, in der Funktion AStern() z.B. ganz am Anfang diesen Befehl zu setzen uns ggf. auch im Code zwischendurch auch nochmal, damit öfter (z.B. in jeder rekursiven Instanz) auf Abbruch von threadname2 getestet wird?
    Ja, genau so ist das gedacht. Vielleicht willst du eine Funktion "pollEvents()" o.ä. definieren, die regelmäßig aufgerufen werden soll, und in der du dann pthread_testcancel() ausführst und außerdem noch alles andere was vielleicht so gelegentlich Mal an Ereignissen abgearbeitet werden soll.

    Grüße,
    Markus
    Tiny ASURO Library: Thread und sf.net Seite

  3. #3
    HaWe
    Gast
    prima, danke für die Rückmeldung!
    Ich hatte befürchtet, pthread_testcancel() würde vlt nur direkt in dem pthread-Thread wirken, in dem der extern ausgelöste pthread_cancel() wirken soll, also "direkt" im Funktionskörper von *threadname2 () ,
    evtl aber vlt nicht mehr in nachgeordneten Funktionen, die ihrerseits nur von dem pthread sekundär angesprungen wurden (und vlt ja nicht "wissen" über die pthread-ID, die hier ggf gemeint bzw. betroffen ist, hier in AStern().
    Wie das AStern "merken soll", welcher übergeordnete Thread beendet werden soll (und damit auch AStern selber) ist mir allerdings noch nicht klar - aber gut, wenn es dann wenigstens funktioniert wie geplant

  4. #4
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    20.08.2008
    Ort
    Karlsruhe
    Alter
    36
    Beiträge
    1.225
    pthread_testcancel() interessiert sich nicht dafür, welche andere Funktion sie aufruft. Jeder Thread hat gewisse Kontextinformationen, und auf diese wird zugegriffen. Du bekommst also immer die Information zu dem Thread der deine Funktion gerade jetzt ausführt. Wenn deine Funktion von zwei Threads ausgeführt wird, kann das Ergebnis also durchaus einmal TRUE und einmal FALSE sein!

    Grüße,
    Markus
    Tiny ASURO Library: Thread und sf.net Seite

  5. #5
    HaWe
    Gast
    neinnein, jede die es betrifft, wird nur 1x von 1 Thread aus angesprungen.
    Ich habe mal eine kleine Simulation geschrieben:

    Einer der Threads startet eine Fibonacci-Berechnung linear (sehr schnell),
    der andere eine andere, rekursive Fibinacci-Funktion (viel langsamer).

    lässt man jetzt die Fibonacci (93) berechnen, ist Thread2-linear schnell fertig (nur zu Simulationszwecken),
    Thread1-rekursiv braucht aber ewig.

    beide Threads können durch einen 3. Thread abgebrochen werden, wenn man ESC drückt (um Thread2-linear zu erwischen, irre schnell ... ).

    Aber egal, ob jetzt in Fibo-Rekursiv ein pthread_testcancel drin steht oder nicht:
    es dauert immer gleich lang bis zum Abbruch des rekursiven , nämlich (bei meinem Linux) ca. 3 sec.

    Eigentlich ist das zu lang, und es müsste auch viel schneller gehen, da sich jede Sekunde mindestens 100x die Funktion selber rekursiv aufruft, und daher theoretisch also auch innerhalb von 10ms abgebrochen werden müsste mit pthread_testcancel() am Anfang eines jeden neuen (rekursiven) Funktionsstarts
    - fast ewig lang allein bis zur Reaktion auf den Abbruch, vergleicht man es mit dem kompletten Durchlauf der gesamten kompletten linearen Funktion bis zur 200. Iteration, erkennbar auch an der ausgegeben printf-ESC-Sequenz im Terminal bis zur letztendlichen Abbruch-Reaktion.

    Sehr effektiv, gelinde gesagt, scheint also pthread_testcancel in der "abhängigen" rekursiven Funktion nicht zu wirken...
    Code:
    /*
     *  Fibonacci numbers
     *  simulaneous competition:
     *  linear vs. recursive calculation
     * 
     */
    
    
    #include <stdio.h>
    #include <stdint.h>
    #include <string.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <termio.h>
    
    #define msleep(ms)  usleep(1000*ms)
    
    pthread_t threadID0, threadID1, threadID2;
    
    long f;
    
    volatile int running1 = 1;
    volatile int running2 = 1;
    
    
    // ********************************************************************
    
    
    bool kbhit(void)
    {
        struct termios original;
        tcgetattr(STDIN_FILENO, &original);
    
        struct termios term;
        memcpy(&term, &original, sizeof(term));
        term.c_lflag &= ~ICANON;
        tcsetattr(STDIN_FILENO, TCSANOW, &term);
    
        int characters_buffered = 0;
        ioctl(STDIN_FILENO, FIONREAD, &characters_buffered);
        tcsetattr(STDIN_FILENO, TCSANOW, &original);
    
        bool pressed = (characters_buffered != 0);
    
        return pressed;
    }
    
    // ********************************************************************
    
    
    inline unsigned long long fibolinear( long n ) {
      volatile static unsigned long long  fibarr[3]; 
      static long i;
    
      fibarr[0]=0;
      fibarr[1]=1;
      fibarr[2]=0;
    
      if(n==0) return 0;
    
      for (i=1; i <= n; i++) {
         
          fibarr[0]=fibarr[1];
          fibarr[1]=fibarr[2];
          fibarr[2]=fibarr[1]+fibarr[0];
          printf("\n order linear: %ld  %llu \n" , i, fibarr[2]);       
      }
      return fibarr[2];
     
    }
    
    
    
    inline unsigned long long fiborecurs( long n ) {
      unsigned long long   llbuf;  
      volatile static long order = 0;
    
      pthread_testcancel(); // <<<<<<<<<<<<<<<<<<<< oder mal weglassen!
    
      if ( n==0 )    { llbuf = 0;  }
      else   
      if ( n==1 )    { llbuf = 1;  }
      else {
        
         llbuf = fiborecurs(n-1) + fiborecurs(n-2);         
         if(n > order) {
             order = n;
             printf("\n order recursive: %ld  %llu \n" , order, llbuf); 
         }     
      }
      return (llbuf); 
    }
    
    
    
    // ********************************************************************
    
    
    
    
    void* threadf2(void *) {
        unsigned long long res;
        int RETVAL = 0;
           
        res = fibolinear(f);
        
        printf("\n\nFibonacci (%ld) (linear) is = %llu \n", f, res);     
        printf("===============================================\n\n"); 
    
        running2 = 0;
        msleep(100);
        
        return (void*)RETVAL; ;
        
    }
    
    
    
    void* threadf1(void *) {
        unsigned long long res;
        int RETVAL = 0;
     
        res = fiborecurs(f);
        
        printf("\n\nFibonacci (%ld) (recursive) is = %llu \n", f, res); 
        printf("===============================================\n\n");    
        
        running1 = 0;
        msleep(100);
        
        return (void*)RETVAL; ;
        
    }
    
    
    
    
    void* threadW0(void *) {
        int RETVAL = 0;
        
        while(running1 || running2) {
     
          if (kbhit())
          {
             int ch = getchar();
             if(ch==27) {       
                // DEBUG    
                printf("\nin pthread0: user kbd press ESC...  ");  
                printf("\npthread0 cancelling pthread 1... ");                         
                if(threadID1) pthread_cancel(threadID1);
                
                // DEBUG 
                printf("\npthread0 cancelling pthread 2...");             
                if(threadID2) pthread_cancel(threadID2);
                
                running1 = 0;
                running2 = 0; 
                
                // DEBUG 
                printf("\nin pthread0: program interrupted by user  \n");
                
                RETVAL = 27;  // user break  
                return (void*)RETVAL; 
             }   
          }   
          msleep(1000);
          printf(".");
          fflush(stdout);
        }
        return (void*)RETVAL; 
        
    }
    
    // ********************************************************************
    
    
    int main() {	   
        void *retval0 = NULL, *retval1 = NULL, *retval2 = NULL;
        
        printf("enter Fibonacci order: ");
        scanf("%ld", &f);      
        
        pthread_create( &threadID1, 0, threadf1, 0 ); 
        pthread_create( &threadID2, 0, threadf2, 0 ); 
        pthread_create( &threadID0, 0, threadW0, 0 ); 
        
             
        pthread_join( threadID0,  &retval0 );
        pthread_join( threadID1,  &retval1 );    
        pthread_join( threadID2,  &retval2 );
        
        // DEBUG
        printf("\nretval0 = %d \n", (int)retval0);
        printf("\nretval1 = %d \n", (int)retval1);
        printf("\nretval2 = %d \n", (int)retval2);
        printf("\nprogram finished \n");
    
        return 0;
        
    }
    Geändert von HaWe (26.10.2016 um 00:25 Uhr)

  6. #6
    Benutzer Stammmitglied
    Registriert seit
    19.05.2015
    Beiträge
    69
    Das liegt aber nicht an pthread_testcancel() sondern an deinem msleep()-Makro, welches den Test auf einen Tastendruck nur einmal in einer Sekunde ausführen läßt.
    Reduzier das doch mal auf 1ms - mal sehen was dann raus kommt.

    Gruss botty
    Geändert von botty (26.10.2016 um 02:28 Uhr)

  7. #7
    HaWe
    Gast
    Ja, du hast Recht, da hatte ich was falsch interpretiert:

    In den printf-Ausgaben kommt meistens ziemlich schnell nach ESC-Tastendruck das ESC als Sequenz
    ^[
    auf den terminal-Screen.
    Ich dachte, das wäre identisch mit dem Zeitpunkt, in dem auch kbhit() und getchar() das ESC lesen (dass die also für das Echo verantwortlich wären), wodurch es dann auf stdout ausgegeben wird, und dann wird ja ohne Verzögerung (keine 1000ms später, sondern sofort!) in die if-Schleife eingesprungen und das pthread_cancel dann auch sofort ausgelöst.

    Sehen konnte man aber, dass zwischen ^[ und dem Programmende noch etliche Zeilen von (zig) rekursiven Fibonacci-Berechnungen ausgeführt und ausgegeben werden.

    Nach dem Test mit msleep(10) kommt der Abbruch allerdings jetzt wirklich unmittelbar.

    Tatsächlich scheint es dann aber so zu sein, dass das kbd-Echo ^[ vom ESC-Tastendruck schneller kommt, als kbhit() oder getchar() davon Kenntnis kriegen, und daher habe ich diese Verzögerung völlig falsch interpretiert. Offenbar macht das kbd-Echo ein von meinem Programm völlig unabhängger Linux-Betriebssystem-Thread oder Daemon, oder wie ist das zu erklären?
    Geändert von HaWe (26.10.2016 um 08:53 Uhr)

  8. #8
    Benutzer Stammmitglied
    Registriert seit
    19.05.2015
    Beiträge
    69
    Hi,

    das Kernthema bei der Verarbeitung deiner Tastatureingaben ist, dass sich der Algo grundsätzlich geändert hat.
    Als du noch Single-Threaded programmiert hast, hattest du eine Endlosschleife, die mehrere Sachen abarbeiten sollte und eine war davon die Tastureingabe. Da die getc Varianten blockierende Funktionen sind, musstest du erst testen ob denn wirklich ein Zeichen vorliegt, das hat kbdhit() für dich erledigt. Erst dann konntest du Lesen, damit der Rest des Systems nicht zum Stehen kommt.
    Jetzt wo du einen separaten Thread für die Taststureingabe hast, führt dies zum Pollen und damit zum unnötigen Verbraten von Rechenzeit. Jetzt muss ein getchar() blockieren, unabhängig davon was die anderen Threads machen.

    Der zweite Aspekt ist, dass unter Linux stdio Line-Buffered ist, soll heißen, dass erst nach einem Return (und damit Line-Feed) die Zeichen die im Puffer stehen an getc und Konsorten weitegereicht werden.
    Du willst aber auf jedes Zeichen sofort reagieren können und so muss man mit Hilfe des Termio-APIs ein wenig nachhelfen:
    Code:
    void* threadW0(void *arg) {
        int RETVAL = 0;
        int ch = 0;
        struct termios original, term;
    
    	tcgetattr(STDIN_FILENO, &original);
    
    	memcpy(&term, &original, sizeof(term));
    	term.c_lflag &= ~ICANON;
    	tcsetattr(STDIN_FILENO, TCSANOW, &term);
    
        while(true) {
    
    		ch = getchar();
        	    	
        	if(ch == 27) {
                // DEBUG    
                printf("\nin pthread0: user kbd press ESC...  ");  
                printf("\npthread0 cancelling pthread 1... ");                         
                if(threadID1) pthread_cancel(threadID1);
                
                // DEBUG 
                printf("\npthread0 cancelling pthread 2...");             
                if(threadID2) pthread_cancel(threadID2);
                
                running1 = 0;
                running2 = 0; 
                
                // DEBUG 
                printf("\nin pthread0: program interrupted by user  \n");
    
                // Terminal zuruecksetzen
                tcsetattr(STDIN_FILENO, TCSANOW, &original);	
        
                RETVAL = 27;  // user break  
                return (void*)RETVAL; 
             }   
        }   
    
        return (void*)RETVAL; 
        
    }
    Du setzt das mit dem Terminal gemeinsam benutzte stdio in den non-caninonical Mode, was dir sofort die Zeichen durchreicht und nicht erst auf ein Return wartet.
    Wichtig ist, dass das zurück gesetzt wird, bevor sich dein Programm beendet, sonst ist das Terminal zerschossen.
    Das Echo, wenn du die Tasten drückst, kommt trotzdem noch vom Terminal her aber die Zeitdifferenz ist jetzt weg, da dieser Thread in getchar() blockiert und sobald ein Zeichen verfügbar ist, einerseits das Terminal es anzeigt, andererseits aber dein Thread aufgeweckt wird und seine Verabeitung machen kann.

    Eine ganz andere Sache ist beim Cancel von Threads, dass du darauf achten musst, dass deine Daten konsistent bleiben. Das heisst, dass du genau hinschauen musst welche Library-Calls, System-Calls oder phtread_testcancel() einen Cancelpoint erzeugen und ob an dieser Stelle nicht Inkonsistenzen erzeugt werden, wenn ein Cancel stattfinden würde.
    Ein ungetestet Beispiel:
    fopen, fprintf, und fclose können Cancelpoints einfügen. Wenn du jetzt z.B. ein Rechenergebnis in eine Darei schreiben möchtest, dann funktioniert dies unter Umständen nicht mehr:
    Code:
      FILE *fp = NULL;
    
      fp = fopen("data.txt", "w");
    
      fprintf("Fib von %ld ist %ld\n", 100, fiborecurs(100));
    
      fclose(fp)
    Nehmen wir an, der Thread läuft durch fopen durch und auch fiborecurs wird angeworfen. Dann ist fp ein offener Dateideskriptor. Wenn jetzt ein anderer Thread ein Cancel anfordert und der Thread, der diese Codepassage enthält entweder in fiborecurs oder fprintf gecancelt wird, bleibt ein offener Dateideskriptor zurück, da fclose nicht mehr erreicht wird.
    Wenn du das ein paar Mal machst, wird irgendwann die Dateideskriptortabelle des Programms überlaufen und das Betriebsystem wird mit einem Fehler und Beendigung des Programms reagieren (oder es passieren andere spannende Sachen ).
    In solchen Passagen musst du ein Cancel unterbinden, also sicher sein das deine Lib- und Sys-Calls keine Cancelpoints sind (man pthreads ist da eine Anlaufstelle) oder du mußt mit pthread_setcancelstate() zeitweise Canceln unterbinden und dann wieder anschalten. Du wirst vermutlich deinen gesamten Code unter diesem Aspekt einer genauen Betrachtung unterziehen müssen.

    Als letzt Bemerkung: fiborecurs ist nicht Multi-Thread fähig,da die Variable order dank des static im Datensegment liegt. Würden mehrere Threads diese Funktion ausführen, gäbs Kuddel-Muddel. Grundsätzlich ist es eine sichere Herangehensweise, derartige Variablen zu vermeiden (oder sehr genau zu wissen warum die da sind und in einem Kommentar über die Funktion zu schreiben, dass sie nicht Multi-Thread fähig ist!).

    Gruss botty

  9. #9
    HaWe
    Gast
    dankeschön für die sehr ausführliche Erklärung, wenn ich auch gestehen muss, jetzt ist es wirklich sääähr schwäääre Kost

    das mit nicht-blockierendem kbhit() habe ich machen müssen, damit er mir im Sekundentakt Punkte auf den Bildschirm schreibt. Fibo rekursiv ab 50 aufwärts dauert Stunden (!), und daher die Punkte, damit man weiß, dass er noch lebt

    Den Rest werde ich bei mir verlinken und speichern unter

    "Wichtig! Nicht erledigt! (eilt net ganz so!)"

    Danke nochmal!


    edit,
    BTW:
    weißt du, wie man eine long long unsigned Variable auf Überlauf testet?

  10. #10
    Benutzer Stammmitglied
    Registriert seit
    19.05.2015
    Beiträge
    69
    Bei unsigned gilt

    Addition:
    Code:
    /* a + b */
    ULLONG_MAX - a < b ==> Ueberlauf
    Subtraktion
    Code:
    /* a - b */
    a < b ==> Unterlauf
    Multiplikation:
    Code:
    /* a * b */
    a > ULLONG_MAX / b ==> Ueberlauf
    Wenn die linke Seite vom "==>" wahr wird folgt rechte Seite.


    Division: Test das Divisor > 0 ist. Und wenn Divisor > Dividend gibt's 'ne 0

    Das sind die Fälle die mir einfallen und die Tests müssen vor der eigentlichen Operation gemacht werden.

    Gruss botty

Seite 1 von 2 12 LetzteLetzte

Ähnliche Themen

  1. .:Anfänger Frage zu Sensoren:.(Neu! Frage zu meinem 1. Progr
    Von oceans94 im Forum Sensoren / Sensorik
    Antworten: 22
    Letzter Beitrag: 06.05.2008, 07:54

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •  

Labornetzteil AliExpress