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