PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : C++: Thread anhalten


mobius
2008-04-23, 11:07:02
Morgen Leute,

ich ärgere mich seit Stunden rum, weil ich keine brauchbaren Informationen zum Anhalten eines Threads finde.

Hier mal mein kleines Thread Testprogramm:


void main(void)
{
test();
printf("\n\n\n Der Hauptaktivitätsträger ist schon lange zu Ende");
}

void test(void)
{
int i, j;
HANDLE hThread[5];
DWORD dwThreadID[5];

//release the threads. Remember, ThreadOne is our main thread
hThread[0] = CreateThread(NULL,0,testprozedur1,(LPVOID)0,0,&dwThreadID[0]);
hThread[1] = CreateThread(NULL,0,testprozedur2,(LPVOID)1,0,&dwThreadID[1]);
hThread[2] = CreateThread(NULL,0,testprozedur3,(LPVOID)2,0,&dwThreadID[2]);
hThread[3] = CreateThread(NULL,0,testprozedur4,(LPVOID)3,0,&dwThreadID[3]);
hThread[4] = CreateThread(NULL,0,testprozedur5,(LPVOID)4,0,&dwThreadID[4]);
//wait for all threads to complete before continuing
WaitForMultipleObjects(5, hThread, TRUE, INFINITE);
//Display a messagebox to show that the Wait state has finished
//MessageBox(NULL,"This Messagebox is to show that all threads have completed.","Messagebox", NULL);
//close handles
CloseHandle(hThread[0]);
CloseHandle(hThread[1]);
CloseHandle(hThread[2]);
CloseHandle(hThread[3]);
CloseHandle(hThread[4]);
printf("\n\n\n bevor die 5-Teilaktivitätsträger zu Ende sind");
//end the main function
}


Die selber erstellten Threads geben Zahlen aus, die länger dauernd als ihnen Zeit zur Verfügung steht. Also wechseln sie sich ab. Soweit alles wunderbar.
Nun will ich nachdem die Threads zu ende sind etwas ausgeben, was allerdings nicht geht, da der Hauptthread zuerst durchrennt und somit beide Prints gemacht werden, bevor die Ausgaben der anderen Threads kommen.
Wie löse ich das Problem? Ich hatte daran gedacht den Hauptthread zu stoppen bis die Ausgaben fertig sind und dann weiterlaufen zu lassen, allerdings finde ich keine brauchbaren Anfängerinformationen zum Stoppen eines Threads. Danke für eure Hilfe.

mapel110
2008-04-23, 11:13:46
Vorab, ich hab keine Ahnung von der Materie.

Aber logischerweise würde ich sagen, dass du nicht den Hauptthread stoppen musst, sondern dass er auf das Ende der anderen an passender Stelle warten soll. Eine Art WAIT oder so sollte es dafür geben.

The_Invisible
2008-04-23, 11:24:02
in java gäbe es die methode join() dafür - http://java.sun.com/docs/books/tutorial/essential/concurrency/join.html

vielleicht hilft es dir weiter bei deiner recherche.

mfg

mobius
2008-04-23, 11:24:48
Gucken mal in die Mitte des Codes ;)
Da findest du genau so einen Befehl und die Kommentierung sagt auch, dass an der Stelle gewartet werden soll. Ich glaube aber, dass das falsch beschrieben ist. Der Hauptthread rennt so oder so durch, aber wenn ich das Warten rausnehme, dürfen die anderen nicht zu ende arbeiten. Das ist wohl die Funktion dieses Befehls, nicht das was ich mir erhofft hatte.

Edit: Es gibt ja einen Stop Befehl "DWORD Stop ( bool bForceKill = false );", aber er tut absolut gar nichts. Die Erklärungen dazu sind auch sehr mangelhaft. Etwas zu finden ist weniger das Problem, vielmehr ein anständiges Beispiel, dass man auch nachvollziehen kann.

Trap
2008-04-23, 11:30:07
Du nimmst in dem Code an, dass printf() in der Reihenfolge Zeug auf die Console schreibt in der es von den verschiedenen Threads aufgerufen wurde. Insbesondere nimmst du an, dass es überhaupt eine definierte Reihenfolge gibt.

Ich bin mir nicht sicher ob das stimmt. Eventuell funktioniert der Kontrollfluss so wie du es haben möchtest, aber die Ausgaben nicht.

mobius
2008-04-23, 11:44:09
Was ich haben will/erwartet habe:

- Main ruft test() auf
- Threads arrangieren sich irgendwie und machen ihre Ausgaben
- Mainthread wartet darauf, dass die anderen fertig sind
- das print in test wird ausgegeben
- print in main wird ausgegeben

Warum die Ausgaben vor den anderen Threads passieren kann ich mir schon vorstellen. Ich habe 1 Hauptthread, der in test läuft, und 5 Workerthreads, welche in test erstellt wurden. Der Hauptthread hat jetzt seine 20ms Zeit zum arbeiten und erstellt auch die anderen Threads, hat aber noch massig Zeit übrig und rennt bis zum Ende.
Soweit könnte ich es nachvollziehen. Allerdings hätte ich an der Stelle erwartet, dass der Mainthread jetzt aufhört und die Worker anfangen und erst danach die Ausgabe der main erfolgt. Warum er zuerst die main komplett abarbeitet und erst dann die Worker an die Arbeit lässt, ist mir etwas schleierhaft. Meine Annahme, dass der Mainthread mit dem Ende von test aufhört, ist wohl falsch. Da muss ich irgendwie intervenieren.

Trap
2008-04-23, 12:20:28
Wenn man in den Workerthreads Datenstrukturen ändert und die dann im Main-Thread ausgibt, sieht man, dass der Main-Thread am WaitFor... auch tatsächlich wartet.
Auch in deinem Beispielcode tut er das.

mobius
2008-04-23, 12:58:50
Er wartet bis sie zu ende sind, aber die Reihenfolge ist nicht wie erwartet. Er rennt erst durchs ganze Programm, bis zum Ende der main und wartet erst an dem Punkt, wo er das Programm beenden will und dann kommt erst die Ausgabe der Workerthreads.

bevor die 5-Teilaktivitõtstrõger zu Ende sind


Der Hauptaktivitõtstrõger ist schon lange zu Ende..............................
...................................................-xo|-ox|-xo|-xo|-xo|-xo|-xo|-
o|x-xo|-xo|-o|x-xo|-ox|-xo|x|-o-ox|x-o|xo-|x-|ox-o|x-o|x-o|xo|-x-o|x-o|x-|ox-o|x
-o|xo|-xo|-x-|ox-|oxo|--o|xx-|ox-o|x-|ox-o|x-o|x-o|x-o|xo|-xo|-xo|-x-o|x-|ox-|ox
o|-xo-|x-o|x-o|-o|xxo-|x-o|x-o|x-o|x-o|x-o|x-o|x-o|xo-|x-o|x-o|x-|oxo-|x-o|xo|-x
-o|xo|--|oxx-o|x-o|xo|-xo|-x-o|x-o|x-o|xo|-x-o|x-o|xo|-Press any key to continue

Das ist meine Ausgabe. Die Zeichen sind von den Workerthreads und die ersten 2 Prints sieht man ja im Quellcode.
Wenn er am waitfor die Workerthreads komplett durchlaufen lassen würde, müssten die Zeichen vor den 2 Prints sein. Oder versteh was komplett falsch?

Trap
2008-04-23, 13:12:43
Oder versteh was komplett falsch?
Ja. printf() aus mehreren Threads aufrufen macht etwas völlig anderes als du haben möchtest. Das Problem ist nur das printf(), der Kontrollfluss ist schon so wie du es haben möchtest.

Eventuell legt es einen Puffer pro Thread an und guckt beim Programm beenden ob noch was drin ist und schreibt das dann raus. Auf jeden Fall ist das Verhalten bei mehreren Threads undefiniert.

noid
2008-04-23, 13:13:20
Damit könntest du weiter kommen.
http://comsci.liu.edu/~murali/win32/Mutex.htm

mobius
2008-04-23, 13:31:30
Ja. printf() aus mehreren Threads aufrufen macht etwas völlig anderes als du haben möchtest. Das Problem ist nur das printf(), der Kontrollfluss ist schon so wie du es haben möchtest.

Eventuell legt es einen Puffer pro Thread an und guckt beim Programm beenden ob noch was drin ist und schreibt das dann raus. Auf jeden Fall ist das Verhalten bei mehreren Threads undefiniert.

omg du hast wirklich recht, allerdings war das Problem cout mit dem ich Ausgaben innerhalb der Workerthreads gemacht habe. Jetzt habe ich sie durch printf´s ersetzt und es funktioniert. Darauf wär ich nie gekommen, danke.
Dennoch bin ich für die Thematik des generellen Anhaltens von Threads interessiert. Das muss ich später wahrscheinlich auch noch einbauen. Wer Links und Beispiele hat, bitte weiter posten.

Ectoplasma
2008-04-23, 13:59:47
Hallo Mobius,

wenn du mit mehreren Threads und der C-Runtime arbeitest, dann solltest du dein Programm mit den Multithreaded Bibliotheken übersetzen. Hast du? Ok ;)

Zweitens: Einen Thread, der mit C-Runtime Funktionen wie 'printf' arbeitet, solltest du nicht mir CreateThread, sondern mit _beginthread oder _beginthreadex starten. Siehe: http://msdn2.microsoft.com/en-us/library/kdzttdcb(VS.71).aspx
Das würde ich mir einmal sehr genau durchlesen.

Einen Thread kann man mit SuspendThread anhalten und mit ResumeThread wieder starten. Allerdings würde ich davon abraten und lieber mit Synchronisationsobjekten arbeiten. Noid hat ja schon einen Link geposted.

Ansonsten kann ich nur sagen, dass es ziemlich einfach ist einen Thread zu starten, aber das korrekte anhalten eines Threads, vorallem wenn durch eine Benutzereingabe ein Programm anhalten soll, während noch Threads laufen, ist eine ganz eigene "Kunst". Viel Vergnügen.

del_4901
2008-04-23, 17:59:49
Also so Schlagwörter wie Queue, Mutex, CAS, Semaphore, Monitor sind schon nicht verkehrt. Bevor der Threadstarter (MUHAHAHAHAHAHA ... das ist mir jetzt erst aufgefallen) jetzt weiter macht, sollte er die Queue, und irgendeins von den Anderen kennen. Achja mit sleep kann man sich schlafen legen, damit keiner busy waiting betreiben muss.

Gast
2008-04-24, 21:29:28
1.) Die Funktion WaitForMultipleObjects habe ich noch nicht verwendet, aber schon WaitForSingleObjects 4 mal hintereinander (habe die Funktion nocht nicht gekannt bzw. dann nicht weiter gesucht). Das mit dem Warten funktioniert definitiv so.

2.) Meines Wissens nach, schreibt printf nicht auf den Bildschirm, sondern lediglich in einen Puffer. Man kann also grundsätzlich nicht davon ausgehen, dass ein Programm eine gewisse Stelle nie erreicht hat, wenn keine Ausgabe da ist. Wenn das Programm z.B. kurz nach einem printf abschmiert, muss es nicht sein, dass das noch am Bildschirm landet. Bei einem \n soll aber angeblich auch der Puffer auf den Bildschirm geschrieben werden. Ich weiß aber nicht mehr, ob sich das auf Windows, Linux oder beides bezogen hat.
Bei sämtlichen IO Operationen des Betriebssystems wird nur ein Puffer verwendet. Das sieht man sehr gut, wenn man Bytes einzeln übers Netzwerk schickt bzw. in eine Datei schreibt. Da braucht er immer einen Context Switch, der um die 10 µs dauert (100K pro Sekunde). Es kann aber sein, dass das printf intern auch auch einmal einen kleinen Puffer anlegt. Das könnte man testen, indem man auf eine schnelle Platte in einer Schleife ein fprintf macht mit jeweils 1 Zeichen und alle 10 Zeichen ein \n. Einmal pro Zeichen schreiben heißt ca. 100KB/s, einmal pro \n schreiben heißt ca. 1MB/s und mehr heißt, dass er noch mehr cached.

3.) Einen Thread anzuhalten ist sowieso eine unsaubere Angelegenheit. Wenn möglich sollte man durch Semaphoren, Mutexe (keine Ahnung wie da die Mehrzahl ist) etc. schauen, dass man regelmäßig überprüft, ob man noch weiterarbeiten soll und wenn nicht die Ausführung sauber beenden. Einfach so CPU wegnehmen ist ungut und nur in machen Fällen Sinn z.B. ein Thread, der nur Connections entgegen nimmt und die meiste Zeit blockiert ist und selbst da gibt es meistens elegantere Lösungen z.B. ein Timeout von 1s beim Annehmen der Connection.

4.) Eine Möglichkeit, wie ich Benutzerausgaben meisten unter VB.NET handle ist, dass ich diese einfach in eine Synchronisierte Queue schreibe (geht dank .NET Framework auch in C++) und dann einen Timer alle 100ms aufrufe, der die Ausgaben ausgibt. Das reicht für den User locker aus.