[gelöst] pthread Fragen



  • Hallo!

    Ich habe ein Problemchen mit pthreads. Und zwar will ich eine Hauptfunktion die sequenziell Steuerungsfunktionen in einem eigenen Thread startet. Während die Steuerungsfunktion läuft befindet sich die Hauptfunktion in einer Schleife und überprüft ob die Steuerungsfunktion ein Zeitlimit überschritten hat. Wenn ja soll der Thread abgeschossen werden.

    Mein Problem mit pthreads ist, dass wenn ich die Threads detachable erstelle ich sie nicht abschiessen kann, und wenn ich sie joinable erstelle, kann ich nicht in einer schleife laufen, da natürlich durch den pthread_join aufruf der Hauptthread erstmal pausiert. Ausserdem habe ich bei den joinable-Threads auch noch das Problem, dass diese nicht komplett aufgeräumt werden, dadurch ist nach ein paar durchläufen feierabend, weil der Prozess keine neuen Threads mehr erstellen kann.

    Ich bin dankbar für alle Hilfen und Vorschläge! Irgendwie muss das doch möglich sein!? Es ist das erste mal, dass ich mit pthreads arbeite.



  • 1. Dein Thread sagt dem Steuerungsthread bescheid wenn er fertig ist, dann macht dieser ein join.

    2. Zeig mal bisherigen Code (compilierbar und lauffähig), der das Problem zeigt.



  • du könntest auch cancellation points einbauen, in regelmäßigen zeitabständen, wenn der thread dort vorbei kommt (sollte er, wenn man sie an die richtigen stellen setzt), wobei die stellen ja auch irgendwie von einer norm vorgegeben sind, weiß jetzt darüber auch nicht mehr, schaut er, ob er abgebrochen werden soll, oder nicht, das heißt, man kann ihn auch mitten in der arbeit unterbrechen.
    außerdem hättest du noch möglichkeiten, das ganze in prozesse auszulagern, macht das ganze speichermanagement unter umständen leichter (allerdings mit shared memory vielleicht auch nicht unbedingt), dann könntest du mit pipes arbeiten, was auch ganz angenehm ist und immer mal ein nicht blockierendes select() sowohl in den hauptprozess, als auch in den kindprozess einbauen, um zu überprüfen, ob der kindprozess noch lebt und diesen zur not beenden



  • Vielen Dank erstmal für die Antworten. Ich hab hier mal ein sauberes Beispiel aus meinem Spaghetti-Code zusammengestellt 😉

    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    #include <sys/signal.h>
    
    static unsigned short running = 0;
    static int timelimit = 1000;
    
    void * StartThread(int threadNr)
    {
    	int i;
    	fprintf(stdout, "child_Thread #%d started...\n", threadNr);
    	sleep(5);
    	running = 0;
    	fprintf(stdout, "child_Thread #%d finished...\n\n", threadNr);
    	pthread_exit(NULL);
    }
    
    void TaskLoop()
    {
    	pthread_t thread;
    	pthread_attr_t attr;
    
    	pthread_attr_init(&attr);
    	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    
    	int i = 0;
    	//for(i = 0; i < 100000; i++)
    	while(1)
    	{
    		i++;
    		fprintf(stdout, "Creating Thread #%d\n", i);
    		running = 1;
    		int errorCode = pthread_create(&thread, &attr, StartThread, i);
    		if (errorCode)
    		{
    			fprintf(stdout, "Error creating thread!\n");
    			running = 0;
    		}
    		else
    		{
    			int timeout = timelimit;
    			while(running)
    			{
    				usleep(10000);
    				timeout -= 10;
    				if (timeout <= 0)
    				{
    					int errorKill = pthread_kill(&thread, SIGALRM);
    					fprintf(stdout, "Killed thread with errorCode: %d\n", errorKill);
    					running = 0;
    				}
    			}
    			pthread_join(&thread, NULL);
    		}
    
    	}
    
    	pthread_attr_destroy(&attr);
    }
    
    int main(int argc, char *argv[])
    {
    	TaskLoop();
    	fprintf(stdout,"Process ended.\n");
    	return EXIT_SUCCESS;
    }
    

    Die Anzahl der Threads die ich erstellen kann scheint nicht mehr begrenzt zu sein. Das einzige was nicht funktioniert, ist dass der laufende Thread abgeschossen wird wenn er das Zeitlimit überschreitet. pthread_kill liefert immer ErrorCode 3 (Thread nicht gefunden) zurück. Auch mit pthread_cancel hatte ich keinen Erfolg.

    Einen Mutex um die Zuweisung von running hatte ich mir jetzt mal vorerst geschenkt.

    Mich würde jetzt also interessieren wie schieße ich den laufenden Thread sauber ab? Ist das mit den Threads so ok? Nicht dass ich irgendwelche Speicherlöcher produziere.

    Danke schonmal !!



  • Zu früh gefreut, wenn man das Sleep in Zeile 13 auskommentiert sieht man, dass nach einer gewissen Zeit doch keine neuen Threads mehr erstellt werden. Es funktioniert also gar nix so wie es soll 😞



  • Also wenn ich das pthread_join in Zeile 53 auskommentiere und den Thread als PTHREAD_CREATE_DETACHED anstatt JOINABLE erstelle, funktioniert zumindest mal dass ich so viele Threads starten kann wie ich will. Aber abschiessen geht immer noch nicht...



  • Du machst einige Fehler in deinem Code:

    1. Auf running wird von verschiedenen Threads schreibend und lesend zugegriffen, deshalb müssen alle Zugriffe während es zwei Threads gibt durch einen Mutex geschützt werden.

    2. Du übergibst einen int an pthread_create obwohl als letztes Argument ein void * erwartet wird. Das geht gar nicht.

    3. Du übergibst einen Zeiger auf pthread_t an pthread_kill und pthread_join, obwohl die den Wert erwarten.

    4. pthread_kill sendet ein Signal und killt nicht.

    5. Man könnte wie oben schon angedeutet pthread_cancel benutzten. Besser ist es aber, wenn der Masterthread dem Child bescheid gibt.

    Eine Lösung mit pthread_cancel, die mir persönlich nicht gefällt wäre folgendes:

    #include <stdio.h>
     #include <stdlib.h>
     #include <pthread.h>
     #include <sys/signal.h>
    
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    
    static unsigned short running = 0;
    
     static int timelimit = 1000;
    
     void * StartThread(void * ptr)
     {
         int threadNr = *((int *) ptr);
         free(ptr);
         fprintf(stdout, "child_Thread #%d started...\n", threadNr);
        // sleep(5);
         pthread_mutex_lock(&mutex);
         running = 0;
         pthread_mutex_unlock(&mutex);
         fprintf(stdout, "child_Thread #%d finished...\n\n", threadNr);
         pthread_exit(NULL);
     }
    
     void TaskLoop()
     {
         pthread_t thread;
         pthread_attr_t attr;
    
         pthread_attr_init(&attr);
         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    
         int i = 0;
         while(1)
         {
             int * id;
             i++;
             fprintf(stdout, "Creating Thread #%d\n", i);
             running = 1;
             id = malloc(sizeof(int));
             *id = i;
             int errorCode = pthread_create(&thread, &attr, StartThread, id);
             if (errorCode)
             {
                 fprintf(stdout, "Error creating thread!\n");
                 running = 0;
                 return;
             }
             else
             {
                 int timeout = timelimit;
                 while (1)
                 {
                     int finish = 0;
                     pthread_mutex_lock(&mutex);
                     if (running == 0) finish = 1;
                     pthread_mutex_unlock(&mutex);
                     if (finish) break;
                     usleep(10000);
                     timeout -= 10;
                     if (timeout <= 0)
                     {
                         int errorKill = pthread_cancel(thread);
                         fprintf(stdout, "Killed thread with errorCode: %d\n", errorKill);
                         pthread_mutex_lock(&mutex);
                         running = 0;
                         pthread_mutex_unlock(&mutex);
                     }
                 }
                 pthread_join(thread, NULL);
             }
    
         }
    
         pthread_attr_destroy(&attr);
     }
    
     int main(int argc, char *argv[])
     {
         TaskLoop();
         fprintf(stdout,"Process ended.\n");
         return EXIT_SUCCESS;
     }
    


  • Vielen Dank Ponto!!!

    Tja die Api müsste man schon lesen können, dass join und cancel keinen Pointer sondern den Wert erwarten hatte ich einfach übersehen. Das mit dem Mutex hatte ich ja wie schon erwähnt aus Faulheit erstmal weggelassen, auch weil es ja eigentlich gar nicht vorkommen soll das beide Threads zeitgleich darauf zugreifen könnten. Aber Du hast natürlich recht, dass da ein Mutex drumherum gehört!

    Ist es nicht egal, dass ich an pthread_create einfach ein integer übergebe? Er erwartet zwar nen Pointer, der hat aber auch die Größe eines int. Und so mißbrauch ich einfach den Wert für den Pointer für ein int!? Ist zwar kein schöner Stil, aber funktionieren tut das ja ohne dass ich was am Speicher verpfusche.

    Dass der Master dem Child bescheid gibt funktioniert leider nicht. Die Funktion soll dazu dienen verschiedene Steuerungsfunktionen (die ich in einem Funktions-Pointer-Array halte) sequenziell auszuführen. Diese Funktionen programmieren andere Personen. Als Ergebnis kommt dann einfach ne 1 oder ne 0 zurück je nachdem ob bestimmte Dinge zulässig sind oder nicht. Das mit dem timeout soll nur für den Fall der Fälle sein, dass eine Funktion mal nicht reagiert weil. Wie gesagt der Fall sollte eigentlich ohnehin nicht eintreten, aber sicher ist sicher.

    Du hast mir sehr geholfen!! Ich war schon am verzweifeln. Bisher hab ich nur in C# mit Threads programmiert, was ja ziemlich idiotensicher ist.

    Vielen Dank nochmal!



  • marekwy schrieb:

    Vielen Dank Ponto!!!
    Ist es nicht egal, dass ich an pthread_create einfach ein integer übergebe? Er erwartet zwar nen Pointer, der hat aber auch die Größe eines int. Und so mißbrauch ich einfach den Wert für den Pointer für ein int!? Ist zwar kein schöner Stil, aber funktionieren tut das ja ohne dass ich was am Speicher verpfusche.

    Ein int ist eben nicht so groß wie ein Zeiger. Das ist auf einigen Maschinen so. Aber zum Beispiel schon auf x86_64 nicht mehr.

    Und in deinem Code greifen beide Threads definitiv gleichzeitig auf die Variable zu. Und du willst sicherstellen, dass die änderungen im anderen Thread sichtbar sind.



  • Ein int ist eben nicht so groß wie ein Zeiger. Das ist auf einigen Maschinen so. Aber zum Beispiel schon auf x86_64 nicht mehr.

    Stimmt natürlich. Werds mir gar nicht erst angewöhnen 😉


Anmelden zum Antworten