PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [C++] Thread, Multithread, Pools - Verwirrung


anakin87
2013-03-12, 13:32:13
Hallo,
es geht mal wieder um etwas wahrscheinlich banales, aber ich komm nicht weiter.

Ok, ein Programm mit mehreren Threads auszustatten geht ja mittlerweile recht einfach da die future- und thread-Lib einem gut helfen (glaube sogar OS unabhänig).
Nur wie kann ich mehrere 100 Aufgaben erledigen ohne Threads zu spamen die an der CPU ein DOS auslösen.
Es gibt da etwas von ?!boost?!, threadpool, welches zwar bei mir läuft, aber ich würde doch gerne den Threads Funktionen mit Parameter übergeben. k.A. wie...


//Nur Testbsp.
#include <boost\threadpool.hpp>
#define cores 4

using namespace boost::threadpool;

pool tp(cores);

for(int i=0;i<345;i++)
{
tp.schedule(&DoStuff);
}


Eigentlich wollte ich keinen Threadpool (zu viel Aufwand dachte ich) aber ich weiß nicht wie ich Threads wieder verwende und wie ich merke dass ein Thread mit seinem Task fertig ist...

Es geht mir mehr um das Verstehen als um eine Codelösung die nur bei diesem Problem hilft.

Soweit ich es ca. verstehe ist, dass ich einen Thread brauche der mir die Worker koordiniert. Welcher auch Tasks an Threads verteilt die mit ihren bereits fertig sind.
Nur wie bekommt der koordinierende Thread es mit, dass einer der 20 anderen mit seinem Task fertig ist?


Beste Grüße

Gast
2013-03-12, 14:05:24
Du wirst Synchronisationmechanismen benötigen, lies mal über Inter Process Communication nach.
http://www.risc.jku.at/people/schreine/papers/rt++-linuxmag1/main.html ist zwar mit Linux aber der Grundgedanke ist immer gleich.

Ich würde mir aber erstmal Gedanken machen ob Threads in deinem Fall wirklich sinnvoll sind. Sind die 100 Aufgaben tatsächlich unabhängig voneinander? Dauern sie überhaupt lange genug damit es sich auszahlt sie in eigenen Threads laufen zu lassen? Ich würde dir empfehlen dich da erstmal noch ein bisschen einzulesen, deine Bedenken von wegen "CPU DOS durch zuviele Threads" oder "Funktionen mit Parametern an Threads übergeben" zeigen dass du nicht wirklich eine Ahnung hast wie Threads eigentlich funktionieren und wofür sie gut sind.

anakin87
2013-03-12, 14:58:43
Ähm ja gebe ich zu, dass ich von Threading unter C++ wenig Ahnung habe ich steige erst in das Thema ein.

Die CPU mit zu vielen Threads zu überfordern ist nicht von der Hand zu weisen, klar mischt der Scheduler vom OS da noch mit, aber ich kann andere Ressourcen dabei blockieren. Und was ist so verwerflich daran eine Funktion mit Parameter zu übergeben? Die Aufgaben sollen alle getrennt voneinander laufen und nicht gegenseitig locken.
Als Bsp. vielleicht 100 Files aus Format A nach Format B konvertieren (pro Konvert darf ca. 1-2min gerechnet werden und das hätte ich dann gerne auf mehreren Cores, ich kann auch 20 Fibonacci-Folgen ausrechnen lassen bei 4 Cores ist das schon mal besser als nur in einem Thread, usw.). Anwendungsmöglichkeiten fallen mir viele ein.

Wenn das ohne MT effizient läuft dann würde ich von Threads absehen...

Was ich nicht verstehe ist, wie ich aus einem Thread die anderen koordiniere (Syncen, Threadsicherheit, usw.). Eigentlich das was die Threadpool-Lib macht. Natürlich kann ich sie auch verwenden ohne die Mechanismen zu hinterfragen. Nur das hilft mir relativ wenig und shot & forget war sowieso noch nie mein Fall...

"Funktionen mit Parametern an Threads übergeben" zeigen dass du nicht wirklich eine Ahnung hast wie Threads eigentlich funktionieren und wofür sie gut sind.
Und das finde ich etwas hart - ich habe mir sehrwohl überlegt, brache ich es, wie und warum...
Darum frage ich ja :(

Gast
2013-03-12, 15:48:51
Ähm ja gebe ich zu, dass ich von Threading unter C++ wenig Ahnung habe ich steige erst in das Thema ein.

Die CPU mit zu vielen Threads zu überfordern ist nicht von der Hand zu weisen, klar mischt der Scheduler vom OS da noch mit, aber ich kann andere Ressourcen dabei blockieren.

Alleine das Betriebssystem im Idle Zustand laufen bereits über 100 Threads - noch keine Anwendungen! Threadswitch kostet zwar etwas Zeit, aber Angst vor zuviel brauchst du da wirklich nicht haben, schon gar nicht bei deinem Anwendungsfall (1-2min Umwandlung pro File)
Und was ist so verwerflich daran eine Funktion mit Parameter zu übergeben?

So funktioniert das nicht, hast du den oben verlinkten Artikel gelesen? Stell dir einen Thread wie eine zweite main() vor, die eben parallel (aus programmiersicht) zu deiner Haupt-main läuft. Wann ist dein Programm zu Ende? Wenn das Ende von der Haupt-main erreicht ist. Wann ist ein Thread zu Ende? Wenn das Ende der "Thread-main" erreicht ist. main() wird gestartet, läuft, und der Code darin wird ausgeführt. Genauso ist es auch bei Threads. Was willst du da Funktionen mit Parametern übergeben?
Die Aufgaben sollen alle getrennt voneinander laufen und nicht gegenseitig locken.
Als Bsp. vielleicht 100 Files aus Format A nach Format B konvertieren (pro Konvert darf ca. 1-2min gerechnet werden und das hätte ich dann gerne auf mehreren Cores, ich kann auch 20 Fibonacci-Folgen ausrechnen lassen bei 4 Cores ist das schon mal besser als nur in einem Thread, usw.). Anwendungsmöglichkeiten fallen mir viele ein.

Wenn das ohne MT effizient läuft dann würde ich von Threads absehen...

Was ich nicht verstehe ist, wie ich aus einem Thread die anderen koordiniere (Syncen, Threadsicherheit, usw.). Eigentlich das was die Threadpool-Lib macht. Natürlich kann ich sie auch verwenden ohne die Mechanismen zu hinterfragen. Nur das hilft mir relativ wenig und shot & forget war sowieso noch nie mein Fall...

S. oben verlinktes Dokument. Ein paar Schlagworte die dir vielleicht weiterhelfen: Inter Process Communication, Semaphor, Mutex, Lock, Race Condition...

Wenn du dirch mit diesen Begriffen auskennst dann kannst du die Frage "Was ich nicht verstehe ist, wie ich aus einem Thread die anderen koordiniere" beantworten.

Und das finde ich etwas hart - ich habe mir sehrwohl überlegt, brache ich es, wie und warum...
Darum frage ich ja :(
Ja sorry war vielleicht etwas schroff, aber ich habe dir immerhin ein Dokument verlinkt wo so einiges erklärt ist. Multithreading ist eben nichts was man "mal eben" macht, das ganze ist kompliziert mit Tücken ohne Ende. Was glaubst du warum so viele Spiele keine ordentliche Multithreading Unterstützung hinbekommen? Das ist schwierig und nichts was man so nebenbei mal erklären kann. Du wirst dich ordentlich damit beschäftigen müssen, das sage ich nicht aus Überheblichkeit sondern das ist eine Tatsache.

anakin87
2013-03-12, 16:04:49
Ok sorry grobes Missverständniss...

Die Threads die du meinst laufen alle ohne Last - ich hab gerade auf 4 logischen Kernen 4 Threads gestartet die alles zu 100% ausgelastet haben und dann reagierte das OS schon etwas schlecht.
Memo an mich selbst "aktive Threadanzahl = cores - 1". Ich muss noch zwischen aktiven und pausierten Threads unterscheiden.

Ja bin noch am lesen - verstehen kann noch dauern.
Irgendwie ist es unter C# "einfacher" die richtigen Keywords dazu und man ist schon quasi Threadsicher... Darum hatte ich den Eindruck, dass ich mal eben so Funktionen samt Parameter Threads übergebe weil es in C# auch so gemacht wird.
Es kommt mir unter C++ so anders vor, dass ich es nicht so gleich nachvollziehen kann.

Ich will ja C++ nicht so nebenbei lernen, sondern umfassend ;)

Gast
2013-03-12, 16:27:46
Ja wenn dein Thread 100% Last erzeugt ist das was anderes - nur kommt das in der Praxis selten vor. Es ist auch nicht das Szenario wofür Threads gedacht sind.

Ja das glaube ich das es in C# leichter ist, aber du darfst nicht vergessen dass du dort die ganzen Details schon wegabstrahiert hast. In C++ gibt es auch Toolkits die dir die ganzen Details wegabstrahieren, aber das Verständnis für die Benutzung brauchst du trotzdem. In C++ ohne High-Level Toolkits ist Multithreading sehr nahe am Betriebssystem Kernel und entsprechend kompliziert. Du solltest wissen was der Unterschied zwischen Prozeß/Thread ist, was es mit Addressräumen auf sich hat (wer kann welche Daten sehen/lesen), was Race Conditions sind, wie sie entstehen und wie man sie vermeidet.

Für den Anfang würde ich dir wirklich empfehlen oben verlinktes Dokument durchzulesen. Unf hier auf die Schnelle noch ein paar Links:
http://en.wikipedia.org/wiki/POSIX_Threads
https://computing.llnl.gov/tutorials/pthreads/
http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html

Die saubere Lösung für dein Problem "100 Dateien umwandeln" ist übrigens nicht "n Umwandlungen parallel starten" sondern die Umwandlung so zu programmieren dass sie n Kerne auslasten kann und die 100 Dateien dann ganz normal eine nach der anderen Umwandeln. ;)

del_4901
2013-03-12, 16:32:56
Nimm einfach OpenMP

PatkIllA
2013-03-12, 17:20:47
Ja wenn dein Thread 100% Last erzeugt ist das was anderes - nur kommt das in der Praxis selten vor. Es ist auch nicht das Szenario wofür Threads gedacht sind.
Das ist genauso ein Szenario wie jedes anderes auch.

Ja das glaube ich das es in C# leichter ist, aber du darfst nicht vergessen dass du dort die ganzen Details schon wegabstrahiert hast. In C++ gibt es auch Toolkits die dir die ganzen Details wegabstrahieren, aber das Verständnis für die Benutzung brauchst du trotzdem. In C++ ohne High-Level Toolkits ist Multithreading sehr nahe am Betriebssystem Kernel und entsprechend kompliziert. Du solltest wissen was der Unterschied zwischen Prozeß/Thread ist, was es mit Addressräumen auf sich hat (wer kann welche Daten sehen/lesen), was Race Conditions sind, wie sie entstehen und wie man sie vermeidet.Das Verständnis braucht man in C# auch, wenn man nicht böse überraschungen erleben will. Wenn man will kann man da auch fast genauso low level rangehen.
Irgendwie ist es unter C# "einfacher" die richtigen Keywords dazu und man ist schon quasi Threadsicher... Darum hatte ich den Eindruck, dass ich mal eben so Funktionen samt Parameter Threads übergebe weil es in C# auch so gemacht wird.So einfach ist das dann doch oft nicht. Selbst Threads erstellen muss und sollte man eigentlich auch nur selten.
Memo an mich selbst "aktive Threadanzahl = cores - 1". Ich muss noch zwischen aktiven und pausierten Threads unterscheiden.
Spätestens wenn IO dabei ist dann passt die einfache Rechnung nicht. Um die idle zeiten im Thread will man sich auch nicht selbst kümmern.

Die saubere Lösung für dein Problem "100 Dateien umwandeln" ist übrigens nicht "n Umwandlungen parallel starten" sondern die Umwandlung so zu programmieren dass sie n Kerne auslasten kann und die 100 Dateien dann ganz normal eine nach der anderen Umwandeln. ;)Die saubere Lösung ist es das Problem passend aufzuteilen. Wenn man immer hunderte Dateien hat ist das die ganz natürliche Aufteilung und wahrscheinlich die mit Abstand am einfachsten zu implementierendste und evtl auch die effizienteste.

Wenn du lernen und verstehen willst was passiert ist das händische ja ok. Ansonsten würde auch ein Toolkit nehmen.

Ectoplasma
2013-03-12, 17:28:43
"Funktionen mit Parametern an Threads übergeben"

Wo ist das Problem? Klar geht soetwas und es ist sehr sinnvoll. Boost macht soetwas schließlich auch. Boost hat da ein paar nette Lösungen parat, die mittels TLS laufen.

Gnafoo
2013-03-13, 06:04:33
Da es Boost ist, kann man mit Sicherheit auch ein Funktor-Objekt an die schedule-Methode übergeben und das Mittel der Wahl lautet bei Boost dann natürlich boost::bind. Vermutlich wird es also irgendwie so gehen:


void fump(int x, int y)
{
std::cout << x << " " << y << std::endl;
}

// ...
tp.schedule(boost::bind(&fump, 5, 7));


Wobei ich boost::bind schon länger nicht mehr verwendet habe, für die Syntax lege ich meine Hand jetzt nicht ins Feuer.

Was das Problem angeht: zunächst einmal sollte man wissen, ob die Parallelisierung hier überhaupt einen Geschwindigkeitsvorteil bringen kann. Wenn es darum geht, Dateien zu verarbeiten (wurde hier ein paar Mal erwählt, aber leider scheint davon nichts mehr im Startposting zu stehen), dann ist das Problem unter Umständen sowieso eher IO-limitiert und wird durch die (naive) Parallelisierung ggf. sogar langsamer, weil die Platte stärker kreuz-und-quer liest. Da ist es meist geschickter, man liest die Dateien sequentiell ein und verarbeitet die Daten dann parallel in einem Threadpool oder schiebt sie in eine parallelisierte Pipeline. In jeden Fall hängt das immer stark vom Problem ab.

Wenn man mit mehreren Threads zusammen arbeiten möchte oder diese koordinieren muss, dann sollte man sich zumindest ansehen, wie man mit einem Mutex/Lock gemeinsam genutzten Speicher schützt (und warum man das muss) und wie sich Threads untereinander mit Events/Semaphoren/Condition Variables koordinieren können. Außerdem sollte man wissen, was ein Deadlock und eine Race Condition ist und wie man diese vermeiden kann.

Da man solche Fehler leicht selber produzieren kann, empfiehlt es sich – wenn vorhanden – auf bestehende High-Level-Mechanismen zurückzugreifen (da muss man vor allem wissen, was einem alles zur Verfügung steht). Wenn es um das Verständnis geht und der Weg wichtiger ist, als das Ziel, dann kann man sich natürlich problemlos mal mit Mutex, Events und co. austoben und selbst eine Pipeline oder einen Threadpool etc. implementieren.

Gast
2013-03-13, 08:57:12
Ach kommt schon, der TE hat mehr als einmal betont dass es ihm ums Verständnis geht - mal zur Erinnerung ein Quote gleich aus dem Anfangspost

Es geht mir mehr um das Verstehen als um eine Codelösung die nur bei diesem Problem hilft.
und ihr kommt ihm hier mit Funktor Objekten (C++ Anfänger anyone? :freak: ), Thread Local Storage und boost. Das ist alles schön und gut wenn man Ahnung hat und bereits weiß worauf man achten mus. Zum Lernen IMHO ungeeignet.

Nimm eine Low-Level Threading Library am besten mit einem einfachen C-Interface und spiel damit herum. Lerne dies Synchronisationsmechanismen kennen: Mutex, Semaphor, Condition Variable. Produziere Deadlocks (!) und vermeide sie mit den Synchronisationsmechanismen. Dann kannst du zu deinem ursprünglichen Prblem zurückkehren :)

anakin87
2013-03-13, 09:37:37
Guten Morgen Leute,

@AlphaTier, ok das kannte ich noch nicht - werde ich mir mal ansehen.

@PatkIllA ja ist mir schon klar dass der C#-Vergleich sehr vereinfacht ist, aber der High-Level Ansatz ist schwer vergleichbar mit dem Low-Level. Ich muss mich da erst mal durcharbeiten ;)

@Gnafoo das mit den Dateien kam erst später als Bsp., aber hier ist schon öfters gekommen ob ich das wirklich brauche. Ja Threading ist nicht so häufig, aber so selten finde ich es auch nicht und rechenintensive Abläufe aufzuteilen schien mir eine gute Idee zu sein. Von Funktor Objekten habe ich schon mal gelesen, bei der Anwendung muss ich aber noch nachschlagen.

@Ectoplasma, ich dachte eigentlich auch, dass es nicht so falsch sein kann (auch vom Stil her). Ich geb ganz ehrlich zu mein Gedankengang war so: "Viele ähnliche, getrennte, rechenintensive Aufgaben und 8 logische Kerne => dann starte 8 Aufgaben gleichzeitig" Das "ähnlich" wollte ich mit Parameter lösen.

@Gast; jep Anfänger ist richtig, aber nicht grundlegend unwissend - also hoffe ich mal :D
Danke für die Links, wenn ich es mal verstanden habe möchte ich nicht das Rad immer neu erfinden... und high-level LIBs helfen dabei super um nicht jedesmal alles neu zu basteln, aber man braucht auch kein so detailiertes wissen dazu.

thx @ all - jetzt muss ich mal das Vorgeschlagene austesten ;D

PatkIllA
2013-03-13, 10:10:07
@PatkIllA ja ist mir schon klar dass der C#-Vergleich sehr vereinfacht ist, aber der High-Level Ansatz ist schwer vergleichbar mit dem Low-Level. Ich muss mich da erst mal durcharbeiten ;)Den LowLevel krams kannst du auch in C# nutzen. Die Nettigkeiten sind alles nur Zusätze so wie halt Libs für c++.
Falls du nicht gleichzeitig mit der neuen Sprache und Multithreading auseinandersetzen willst.

Thunderhit
2013-03-14, 00:09:35
Ab Visual Studio 2010 gibts dafür das Concurrency-Framework, was die Parallelisierung geradezu trivial macht, wenn man Blockierungen etc. im Griff hat und nicht wie wild alles parallel macht.
Alternativ gibts noch OpenMP und Intels TBB.
Im Prinzip ist es doch so, dass der Threadpool der Bibliothek sich um die Abarbeitung der Aufgaben kümmert in der ihm sinnvollen Reihenfolge.
Beim Concurrencyframework sind die Aufgaben in sogenannte Tasks gekapselt und deren Abarbeitungsstatus kann man dann abprüfen. Sowas müsste es dann auch bei Boost geben.

anakin87
2013-03-19, 11:55:24
ja also bei boost gibt es die Threadpool - Lib, eben die die ich im Eingangsbeitrag angegeben habe. Nur dachte ich es wäre zu unflexibel, weil in den Tuts keine Parameter übergeben wurden, dass Problem konnte ich aber nun mit bind lösen (Gnafoos Posting). Das Concurrency-Framework ist MS gebunden schätze ich mal, muss ich mir aber noch ansehen. thx