[boost] Mutex will nicht so wie ich
-
Wie der Titel schon sagt macht der Mutex nicht das was ich will, was aber eher an mir liegt

Mein Programm sieht so aus:#include "stdafx.h" int tcount = 0; boost::timed_mutex mutex; int mintime; int maxtime; int maxdiv; int interval; std::vector<std::string> values; void threadfunc(void) { /* mutex.try_lock(); tcount++; int tid = tcount; std::cout << tid << std::endl; mutex.unlock(); */ int tid; boost::unique_lock<boost::timed_mutex> lock(mutex, boost::try_to_lock); do { if (!lock.owns_lock()) lock.timed_lock(boost::get_system_time() + boost::posix_time::seconds(1)); else { tcount++; tid = tcount; std::cout << "Thread " << boost::this_thread::get_id() << ": " << tid << std::endl; } } while(!lock.owns_lock()); lock.unlock(); for (int i = 0; i < interval * tid; i++) { for (int j = 1; j <= maxdiv; j++) { srand(i); int v = rand() % j; std::stringstream ss; ss << v << "\n"; values.push_back(ss.str()); } } } int _tmain(int argc, _TCHAR* argv[]) { if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) { std::cout << "Priorität erhöhen fehlgeschlagen" << std::endl; getchar(); return 0; } SYSTEM_INFO sysinfo; GetNativeSystemInfo(&sysinfo); int cores = sysinfo.dwNumberOfProcessors; std::cout << "Anzahl der Threads: " << cores << std::endl; std::cout << "Minimale Systemzeit" << std::endl; std::cin >> mintime; std::cout << "Maximale Systemzeit" << std::endl; std::cin >> maxtime; std::cout << "Maximale Division (keine 0)" << std::endl; std::cin >> maxdiv; if (maxdiv < 1) maxdiv = 1; int delta = maxtime - mintime; interval = delta / cores; std::cout << "Interval: " << interval << std::endl; std::vector<boost::thread> threads; timer time; time.init(); time.begin(); for (int i = 0; i < cores; i++) { threads.push_back(boost::thread(threadfunc)); threads[i].join(); } double deltatime = time.stop(); std::cout << deltatime << std::endl; std::cin.clear(); std::cin.ignore(std::cin.rdbuf()->in_avail()); getchar(); return 0; }Kurz gesagt soll das Programm einfach nur eine Menge Zahlen in verschiedenen Threads berechnen.
Aber die Threads laufen nicht nebeneinander sondern hintereinander, was eigentlich nur an dem Mutex liegen kann. Allerdings weiß ich jetzt nicht, wie ich dem Thread sonst eine einmalige Zahl von 1 bis Anzahl der Kerne geben kann und wieso der zweite Thread wartet, bis der erste Thread mit den Schleifen fertig ist.Also weiß jemand, wie ich jedem Thread eine bestimmte Zahl geben kann? Wenn ich es ohne Mutex einfach nur mit hochzählen versuche haben die Threads nacher z.B. diese Werte: 0, 0, 3, 5, 5, 6, 8, 8 und beim erstellen einen Wert übergeben geht bei Threads anscheinend auch nicht (hab bei google noch nichts wirkliches dazu gefunden).
-
Hi piccolini,
Es liegt nicht am Mutex.
Du rufst
threads[i].join();auf, wodurch mit der Ausführung gewartet wird bis der Thread beendet ist bevor
die Schleife wieder durchlaufen wird.Gruß,
XSpille
-
Danke für den Hinweis. Aber wie lasse ich dann zwei Threads parallel laufen? In der Referenz finde ich nichts.
EDIT: Wenn ich kein join benutze laufen sie anscheinden schon parallel. Aber wie schaffe ich es, dass die main Funktion dann nicht weiterläuft?
-
Pikkolini schrieb:
Danke für den Hinweis. Aber wie lasse ich dann zwei Threads parallel laufen? In der Referenz finde ich nichts.
EDIT: Wenn ich kein join benutze laufen sie anscheinden schon parallel. Aber wie schaffe ich es, dass die main Funktion dann nicht weiterläuft?
for (int i = 0; i < cores; i++) { threads.push_back(boost::thread(threadfunc)); } // Jetzt laufen alle Threads // Sammle die Threads wieder ein... for (int i = 0; i < cores; i++) { threads[i].join(); }Das sollte klappen. Ist aber ungetestet.
-
Hi Pikkolini,
ich hoffe dieses Beispiel hilft dir:
#include<vector> #include<iostream> #include<boost/thread.hpp> #include<boost/function.hpp> boost::mutex mutex; class MyThread { public: MyThread(int par); MyThread(const MyThread& thread); void startThread(); private: int par; }; MyThread::MyThread(int par) : par(par){ } MyThread::MyThread(const MyThread& thread) : par(thread.par){ std::cout << "Copying ThreadInfo" << std::endl; } void MyThread::startThread(){ for(int i=0; i<1000; ++i){ boost::lock_guard<boost::mutex> lg(mutex); std::cout << this->par << ": " << i << std::endl; } boost::lock_guard<boost::mutex> lg(mutex); std::cout << "Thread " << this->par << " succesfully executed" << std::endl; } int main(int argc, char* argv[]) { boost::thread_group threads; for(int i=0; i<5; ++i){ MyThread myThread(i); threads.create_thread(boost::bind(&MyThread::startThread,myThread)); } threads.join_all(); return 0; }Dein Problem mit der Übergabe eines Parameters ist darin auch enthalten.
Der Mutex wir nur verwendet um die Ausgabe auf der Konsole zu synchronisieren.Gruß,
XSpilleEDIT: Jetzt mit boost::thread_group
-
Ich habe es jetzt so wie du ausprobiert und nebenbei aus dem vector eine thread_group gemacht, die so aussieht:
boost::thread_group threads; for (int i = 0; i < cores; i++) threads.create_thread(threadfunc); threads.join_all();Aber bei beiden Methoden werden die Threads hintereinander abgefertigt. Oder muss zwischen die beiden Schleifen noch irgendein Breakpoint für die Mainfunktion?
EDIT: Hab jetzt auch den Beitrag von XSpille gelesen und klingt vielversprechend

Da muss ich mich jetzt mal näher mit beschäftigen.
-
ProgChild schrieb:
for (int i = 0; i < cores; i++) { threads.push_back(boost::thread(threadfunc)); } // Jetzt laufen alle Threads // Sammle die Threads wieder ein... for (int i = 0; i < cores; i++) { threads[i].join(); }Das sollte klappen. Ist aber ungetestet.
Die boost-Doku sagt Folgendes:
Boost-Doku schrieb:
objects of type boost::thread are not copyable.
http://www.boost.org/doc/libs/1_44_0/doc/html/thread/thread_management.html
Folglich wirst du (meiner Meinung nach zumindest ohne C++0x) keine
boost::thread-Objekte in den Vektor packen können...
Mein Compiler meckert auch....
-
Hmm mein Compiler meckert nicht

Aber ich benutze jetzt eh thread_group. Jetzt muss ich nurnoch deine Methode ausprobieren und dann meld ich mich wieder.
-
Pikkolini schrieb:
Hmm mein Compiler meckert nicht

Aber ich benutze jetzt eh thread_group. Jetzt muss ich nurnoch deine Methode ausprobieren und dann meld ich mich wieder.Das Beispiel oben verwendet jetzt auch thread_group.
-
So, jetzt habe ich es so wie XSpille gemacht und die Threads laufen jetzt alle schön nebeneinander

Allerdings wird mein Programm immer langsammer, je mehr Threads ich habe auch bei einem Mehrkernprozessor mit Hyperthreading.
Wenn ich bei einem Intel i7 720QM bei den ersten drei Werten in meinem Programm 0 100 und 100 eingebe braucht er mit einem Thread nur 0,0005 Sekunden, bei vier Threads schon 0,5 Sekunden und bei acht Threads (wegen Hyperthreading) dauert es ganze zehn Sekunden
Die zehn Sekunden bei acht Threads lasse ich jetzt mal außen vor, da ich schon öfters gehört habe, dass Hyperthreading nicht wirklich was schneller macht sondern nur langsamer. Aber das das Programm auch bei vier Threads langsamer ist wundert mich ein bisschen.
-
Pikkolini schrieb:
Allerdings wird mein Programm immer langsammer, je mehr Threads ich habe auch bei einem Mehrkernprozessor mit Hyperthreading.
Wenn ich bei einem Intel i7 720QM bei den ersten drei Werten in meinem Programm 0 100 und 100 eingebe braucht er mit einem Thread nur 0,0005 Sekunden, bei vier Threads schon 0,5 Sekunden und bei acht Threads (wegen Hyperthreading) dauert es ganze zehn Sekunden
Die zehn Sekunden bei acht Threads lasse ich jetzt mal außen vor, da ich schon öfters gehört habe, dass Hyperthreading nicht wirklich was schneller macht sondern nur langsamer. Aber das das Programm auch bei vier Threads langsamer ist wundert mich ein bisschen.Also... Wenn du Code richtig parallelisierst, wird er schneller auf einem Mehrkernsystem laufen. Das bedeutet, du machst was falsch.
Ich habe deinen Ursprünglichen Code mal überflogen. Und durch deinen Mutex wird dein eigentlich paralleles Programm wieder komplett serialisiert. Nur dass jetzt halt noch ein Overhead durch die Threads hinzukommt. Zumindest für die erste Schleife.
In der zweiten Schleife greifst du aus allen Threads ohne Mutex geschützt auf den gleichen vector zu. Das ist auch keine gute Idee.
-
Pikkolini schrieb:
..., dass Hyperthreading nicht wirklich was schneller macht sondern nur langsamer.
Woher hast du den Unsinn schon wieder?
Pikkolini schrieb:
Allerdings wird mein Programm immer langsammer, je mehr Threads ich habe ...
Erstaunt mich irgendwie nicht. Einfach nur ein paar parallel laufende Threads einzurichten, heisst meistens nicht, dass das Programm schneller wird. Das ist ein grosser Irrtum von vielen. Du hast zum Beispiel sehr viele Locks in deinem Code. Das sind so viele, dass die Thread grundsätzlich so gut wie nichts parallel machen. Dadurch hast du am Ende grundsätzlich zig Threads, welche alle seriell die Aufgabe abarbeiten, weil sie sich immer gegenseitig blockieren. Und das blockieren kostet jeweils auch noch massiv Zeit.
Ich stelle wieder mal fest, du machst völlig seltsame Messungen. Profiling, erst recht Profiling über Threads und Performanceoptimierungen über Threads ist ein sehr fortgeschrittenes Thema. Das was du hier probierst ist blanker Unsinn.
Grüssli
-
Dravere schrieb:
Pikkolini schrieb:
..., dass Hyperthreading nicht wirklich was schneller macht sondern nur langsamer.
Woher hast du den Unsinn schon wieder?
Den Unsinn habe ich schon in vielen Foren gelesen, da viele Leute meinten, dass sich zwei Threads trotzdem noch ein Kern teilen müssen und so aufeinander warten müssen. Ob das stimmt keine Ahnung.
Und nun zum Programm. Ich verwende momentan keine Mutex und mit mehreren Threads greife ich auch nicht auf das gleiche zu. Hier mal mein Vollständiger Code:
// MyThread.h class MyThread { public: MyThread(int tid, int interval, int maxdiv); void startThread(void); std::vector<std::string> getValues(void); private: int tid; int interval; int maxdiv; std::vector<std::string> values; }; // MyThread.cpp MyThread::MyThread(int tid, int interval, int maxdiv) { this->tid = tid; this->interval = interval; this->maxdiv = maxdiv; }; void MyThread::startThread(void) { for (int i = 0; i < interval * tid; i++) { for (int j = 1; j <= maxdiv; j++) { srand(i); int v = rand() % j; std::stringstream ss; ss << v << "\n"; values.push_back(ss.str()); } } }; std::vector<std::string> MyThread::getValues(void) { return values; }; // Random.cpp int _tmain(int argc, _TCHAR* argv[]) { if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) { std::cout << "Priorität erhöhen fehlgeschlagen" << std::endl; getchar(); return 0; } SYSTEM_INFO sysinfo; GetNativeSystemInfo(&sysinfo); int cores = sysinfo.dwNumberOfProcessors; std::cout << "Anzahl der Threads: " << cores << std::endl; std::cin >> cores; // nur Testzwecke std::cout << "Minimale Systemzeit" << std::endl; int mintime; std::cin >> mintime; std::cout << "Maximale Systemzeit" << std::endl; int maxtime; std::cin >> maxtime; std::cout << "Maximale Division (keine 0)" << std::endl; int maxdiv; std::cin >> maxdiv; if (maxdiv < 1) maxdiv = 1; int delta = maxtime - mintime; int interval = delta / cores; std::cout << "Interval: " << interval << std::endl; std::vector<std::string> values; boost::thread_group threads; timer time; time.init(); time.begin(); for(int i = 0; i < cores; ++i){ MyThread myThread(i, interval, maxdiv); threads.create_thread(boost::bind(&MyThread::startThread,myThread)); } threads.join_all(); double deltatime = time.stop(); std::cout << deltatime << std::endl; std::cin.clear(); std::cin.ignore(std::cin.rdbuf()->in_avail()); getchar(); return 0; }Die Funktion getValues steht da jetzt einfach so, aber ich brauch sie nicht wirklich.
Dravere schrieb:
Ich stelle wieder mal fest, du machst völlig seltsame Messungen. Profiling, erst recht Profiling über Threads und Performanceoptimierungen über Threads ist ein sehr fortgeschrittenes Thema. Das was du hier probierst ist blanker Unsinn.
Wieso seltsame Messungen? Ich messe nur die Zeit, und darauf kommt es in meinem Programm ja an.
Dravere schrieb:
Erstaunt mich irgendwie nicht. Einfach nur ein paar parallel laufende Threads einzurichten, heisst meistens nicht, dass das Programm schneller wird. Das ist ein grosser Irrtum von vielen.
Anscheindend gehör ich zu den vielen

Aber was gehört dann denn noch zu Performanceoptimierung dazu?Dravere schrieb:
Das was du hier probierst ist blanker Unsinn.
Wieso ist Parallelisierung zu probieren blanker unsinn?
-
Pikkolini schrieb:
Dravere schrieb:
Pikkolini schrieb:
..., dass Hyperthreading nicht wirklich was schneller macht sondern nur langsamer.
Woher hast du den Unsinn schon wieder?
Den Unsinn habe ich schon in vielen Foren gelesen, da viele Leute meinten, dass sich zwei Threads trotzdem noch ein Kern teilen müssen und so aufeinander warten müssen. Ob das stimmt keine Ahnung.
Es stimmt nicht. Die Idee hinter Hyperthreading ist folgende. Laufen zwei Threads und der eine wartet muss auf Daten aus dem Hauptspeicher warten (der ist langsam), so kann der andere laufen und Berechnungen durchführen. Die zwei Threads laufen also nie gleichzeitig, aber trotzdem kann es ein Programm schneller machen.
Pikkolini schrieb:
Und nun zum Programm. Ich verwende momentan keine Mutex und mit mehreren Threads greife ich auch nicht auf das gleiche zu.
values.push_back(ss.str());Doch tust du. Genau an der obigen stelle. Und genau da kann eine Menge schief gehen. Mit großer Wahrscheinlichkeit steht, wenn alle Threads fertig sind, nicht das im Vektor, was vorher rein geschrieben wurde.
Und noch eine Stelle
int v = rand() % j;Die Funktion
randist nicht thread-save. Wenn du aus mehreren Threads die Funktionrandaufrufst, zerstörst du die internen Strukturen der Funktion.Pikkolini schrieb:
Dravere schrieb:
Erstaunt mich irgendwie nicht. Einfach nur ein paar parallel laufende Threads einzurichten, heisst meistens nicht, dass das Programm schneller wird. Das ist ein grosser Irrtum von vielen.
Anscheindend gehör ich zu den vielen

Aber was gehört dann denn noch zu Performanceoptimierung dazu?Du kannst mehrere Threads schon verwenden, um ein Programm schneller zu machen. Mal abgesehen von den Fehlern oben, wird dein Programm nicht schneller durch mehrere Threads, weil der Hauptspeicher nicht schneller wird. Und der ist wahrscheinlich der Flaschenhals bei der Anwendung.
Je nach dem, was du für einen Prozessor hast, können die dauernden Cache-Synchronisationen deinen Code auch noch stark ausbremsen.
-
ProgChild schrieb:
Pikkolini schrieb:
Und nun zum Programm. Ich verwende momentan keine Mutex und mit mehreren Threads greife ich auch nicht auf das gleiche zu.
values.push_back(ss.str());Doch tust du. Genau an der obigen stelle. Und genau da kann eine Menge schief gehen. Mit großer Wahrscheinlichkeit steht, wenn alle Threads fertig sind, nicht das im Vektor, was vorher rein geschrieben wurde.
Jeder Thread hat einen eigenen Vektor. Deine Aussage ist daher (wie ich denke) falsch.
ProgChild schrieb:
Und noch eine Stelle
int v = rand() % j;Die Funktion
randist nicht thread-save. Wenn du aus mehreren Threads die Funktionrandaufrufst, zerstörst du die internen Strukturen der Funktion.
Genau... Hier ist der kritische Punkt...Wenn du folgende Zeile
for (int i = 0; i < interval * tid; i++)änderst in
for (int i = 0; i < interval * tid; i+=tid)dann wird mein Programm nur (etwa) linear langsamer, weil die zu berechnenden Daten ebenfalls linear wachsen.
Nach der Synchronisierung des Zufallszahlengenerators wird es jedoch wohl etwas anders aussehen.
-
ProgChild schrieb:
Pikkolini schrieb:
Dravere schrieb:
Pikkolini schrieb:
..., dass Hyperthreading nicht wirklich was schneller macht sondern nur langsamer.
Woher hast du den Unsinn schon wieder?
Den Unsinn habe ich schon in vielen Foren gelesen, da viele Leute meinten, dass sich zwei Threads trotzdem noch ein Kern teilen müssen und so aufeinander warten müssen. Ob das stimmt keine Ahnung.
Es stimmt nicht. Die Idee hinter Hyperthreading ist folgende. Laufen zwei Threads und der eine wartet muss auf Daten aus dem Hauptspeicher warten (der ist langsam), so kann der andere laufen und Berechnungen durchführen. Die zwei Threads laufen also nie gleichzeitig, aber trotzdem kann es ein Programm schneller machen.
Ok danke. Würde es dann in meinem Programm Sinn machen oder nicht?
ProgChild schrieb:
Pikkolini schrieb:
Und nun zum Programm. Ich verwende momentan keine Mutex und mit mehreren Threads greife ich auch nicht auf das gleiche zu.
values.push_back(ss.str());Doch tust du. Genau an der obigen stelle. Und genau da kann eine Menge schief gehen. Mit großer Wahrscheinlichkeit steht, wenn alle Threads fertig sind, nicht das im Vektor, was vorher rein geschrieben wurde.
Wird in der for-Schleife nicht für jeden Thread ein eigenes Klassenobjekt erstellt? Wenn die for-Schleife acht mal durchläuft, werden doch auch acht unterschiedliche Objekte erstellt oder nicht?
ProgChild schrieb:
Und noch eine Stelle
int v = rand() % j;Die Funktion
randist nicht thread-save. Wenn du aus mehreren Threads die Funktionrandaufrufst, zerstörst du die internen Strukturen der Funktion.Gibts denn eine Alternative dafür? rand_s suche ich nicht, da das die eigentlich Idee meines Programm zerstören würde.
ProgChild schrieb:
Pikkolini schrieb:
Dravere schrieb:
Erstaunt mich irgendwie nicht. Einfach nur ein paar parallel laufende Threads einzurichten, heisst meistens nicht, dass das Programm schneller wird. Das ist ein grosser Irrtum von vielen.
Anscheinend gehör ich zu den vielen

Aber was gehört dann denn noch zu Performanceoptimierung dazu?Du kannst mehrere Threads schon verwenden, um ein Programm schneller zu machen. Mal abgesehen von den Fehlern oben, wird dein Programm nicht schneller durch mehrere Threads, weil der Hauptspeicher nicht schneller wird. Und der ist wahrscheinlich der Flaschenhals bei der Anwendung.
Je nach dem, was du für einen Prozessor hast, können die dauernden Cache-Synchronisationen deinen Code auch noch stark ausbremsen.
Das der Hauptspeicher nicht schneller wird ist klar. Aber wird der schon der Flaschenhals, wenn ich auch nur eine Variable schreiben will?
-
XSpille schrieb:
Wenn du folgende Zeile
for (int i = 0; i < interval * tid; i++)änderst in
for (int i = 0; i < interval * tid; i+=tid)dann wird mein Programm nur (etwa) linear langsamer, weil die zu berechnenden Daten ebenfalls linear wachsen.
Ich Idiot

Diese Zeile:for (int i = 0; i < interval * tid; i++)muss natürlich so heißen:
for (int i = interval * tid - interval; i < interval * tid; i++)Ansonsten muss ja jeder neue Thread zu dem alten, schon berechneten, nochmehr rechnen, was ja ziemlich sinnlos ist. DIe Threads sollen sich die Arbeit ja teilen. Nach der Änderung ist mein Programm mit mehreren Threads direkt mal um einiges schneller.
Jetzt bleibt eigentlich nurnoch das Problem, dass rand() nicht Threadsicher ist...
-
Pikkolini schrieb:
Jetzt bleibt eigentlich nurnoch das Problem, dass rand() nicht Threadsicher ist...
Stichwort: rand_r
-
rand_r klingt in der Theorie zwar ziemlich vielversprechend, aber laut google muss ich dazu die cstdlib einbinden. Doch dort ist rand_r nicht definiert...
-
Warum verwendest du nicht konsequent boost?
Du brauchst dann natürlich ein
boost::mt19937 gen;mit unterschiedlichen seeds pro Thread.
-
Weil ich alle random Zahlen mit verschiedenen Seeds und Divisionen auflisten will. rand_s() oder boost helfen mir da nicht weiter und rand_r() scheint nur in der Theorie zu existieren, da die C++ Referenz und die MSDN das auch nicht gelistet haben.