For loop und pthread
-
Hallo ihr Lieben,
nach langer Zeit habe ich auch mal wieder eine Frage:
Ich versuche mich gerade am Parallelisieren mittels pthread und möchte dabei direkt ein paar Argumente übergeben.
Hier der Quellcode:
#include <iostream> #include <pthread.h> #include <string> #include <vector> struct DATA { DATA( const std::string& s, const unsigned int& i): dummy( s ), id(i) {}; const std::string dummy; const unsigned int id; }; void* foo(void* arg) { DATA* data = static_cast<DATA*>(arg); std::cout << "Thread Nr. " << data -> id << " gives " << data -> dummy << std::endl; pthread_exit(NULL); } int main() { std::vector<std::string> dummies = { "some","stuff","in","here" }; pthread_t threads[ dummies.size() ]; for( unsigned int i = 0; i < dummies.size(); ++i ) { DATA data( dummies[ i ], i ); pthread_create( &threads[i], NULL, foo, (void *)&data ); } pthread_exit(NULL); return 0; }Der Knackpunkt ist, dass die Ausgabe dabei Müll ist, z.B.
Thread Nr. Thread Nr. 33 gives gives herehere 3read Nr. Thread Nr. 3 gives hereH,D\�`�<� �<��`��� ����`{<� {<��`k�� k��!�f�f�<�s��f�f�<�s�Dabei habe ich mich an diesem Beispiel orientiert.
Jetzt kommt aber das schöne, ich habe noch dieses Tutorial gefunden und da wird explizit darauf hingewiesen, dass in C++, der Syntax von
fooanders aussehen soll, also passe ich entsprechend an:void foo(void* arg) { ... pthread_create( &threads[i], NULL, (void*)&foo, (void *)&data ); ...Dafür kriege ich jetzt die Fehlermeldung
warning: ISO C++ forbids casting between pointer-to-function and pointer-to-object [enabled by default] error: invalid conversion from ‘void*’ to ‘void* (*)(void*)’ [-fpermissive]So, und wenn ich danach suche, dann kriege ich irgendwie alles mögliche an 'richtig' und 'falsch'.
Kann mir bitte jemand erklären was genau das Problem ist?
Viele Grüße,
Klaus.
-
Hi!
Ohne mich zu sehr mit dem Code befasst zu haben, zwei Anmerkungen zu deinem Code:
1. Die Textausgabe via
coutim Thread-Code ist nicht synchronisiert. Es ist daher je nach Implementation völlig normal, dass die Ausgabe verschiedener Threads ineinander verschachtelt ist. Um das zu vermeiden, solltest du vielleicht dascoutmit einem Mutex schützen, so dass immer nur ein Thread eine "Zeile" text ausgeben kann.2. Beim Erstellen des Threads in Zeile 38 übergibst du einen Pointer auf
data, bei dem es sich jedoch um eine lokale Variable handelt, die höchstens bis zum abschließenden "}" der Schleife lebt. Die Threads geben daher Daten nicht mehr existierender Objekte aus. Ich vermute, dass bei jedem Schleifendurchlauf derselbe (Stack-) Speicherbereich verwendet wird, um dieDATA-Instanz zu erzeugen, daher geben auch alle Threads die Nummer 3 aus - wobei es Zufall ist, dass das überhaupt halbwegs funktioniert, da wie gesagt, die übergebenen DATA-Objekte nichtmehr existieren, wennfoo()im anderen Thread zur Ausführung kommt.Gruss,
Finnegan
-
Hallo Finnega,
Finnegan schrieb:
Hi!
1. Die Textausgabe viacoutim Thread-Code ist nicht synchronisiert.Ja, no problem with that so far!

Finnegan schrieb:
2. Beim Erstellen des Threads in Zeile 38 übergibst du einen Pointer auf
data, bei dem es sich jedoch um eine lokale Variable handelt, die höchstens bis zum abschließenden "}" der Schleife lebt. Die Threads geben daher Daten nicht mehr existierender Objekte aus. Ich vermute, dass bei jedem Schleifendurchlauf derselbe (Stack-) Speicherbereich verwendet wird, um dieDATA-Instanz zu erzeugen, daher geben auch alle Threads die Nummer 3 aus - wobei es Zufall ist, dass das überhaupt halbwegs funktioniert, da wie gesagt, die übergebenen DATA-Objekte nichtmehr existieren, wennfoo()im anderen Thread zur Ausführung kommt.Daran bastle ich auch gerade herum. Ich habe jetzt vorher einen Vektor erzeugt, der alle relevanten
DATAerhält und in demfor-loopdann mittels des Laufindex darauf zugegriffen wird.Ich probier mich also weiter ...

Gruß,
Klaus.
-
Klaus82 schrieb:
Daran bastle ich auch gerade herum. Ich habe jetzt vorher einen Vektor erzeugt, der alle relevanten
DATAerhält und in demfor-loopdann mittels des Laufindex darauf zugegriffen wird.Ich habe selbst auch schon öfter mit solchen C-Interfaces zu tun gehabt, denen man User-Daten nur über einen
void*übergeben konnte, und bei denen die benötigte Lebensdauer der Objekte nicht genau vorherbestimmbar war (asynchrone Verarbeitung in anderen Threads), und auch noch mehrere Threads auf die selben Objekte zugreifen mussten. In diesem Fall hat sich oft einstd::shared_ptrbewährt, den ich - was einem auf den ersten Blick die Haare zu Berge stehen lassen sollte - vianewerzeugt habe:auto object = std::make_shared<Object>(); ... std::shared_ptr<Object>* userData = new std::shared_ptr<Object>(object); asynchrones_c_interface(userData);... das allerdings nur um den
shared_ptrdurch das C-Interface zu "tunneln". In der Thread-Funktion (bei der garantiert sein sollte, dass diese auch ausgeführt wird, sonst leckt es) wurde dieser dann direkt gelöscht, um möglichst wenig mit nackten, "owning" Pointern herumhantieren zu müssen:void thread_function(void* userData) { auto objectPtr = static_cast<std::shared_ptr<Object>*>(userData); std::shared_ptr<Object> object = *objectPtr; delete objectPtr; ... // mach was mit object. }Vielleicht ist ja ein ähnliches Pattern auch für dein Program nützlich.
Ansonsten empfiehlt sich vielleicht auch eher,std::threadzu benutzen, falls du darauf Zugriff hast, dort kann man dann dieshared_ptrdirekt an die Thread-Funktion übergeben ohne dieses etwas seltsameshared_ptr-Pointer-Gedöns
Gruss,
Finnegan
-
Finnegan schrieb:
void thread_function(void* userData) { auto objectPtr = static_cast<std::shared_ptr<Object>*>(userData); std::shared_ptr<Object> object = *objectPtr; delete objectPtr; ... // mach was mit object. }Vielleicht gibt es Fälle, wo man einen shared_ptr braucht, aber der default sollte unique_ptr sein.
auto object = std::make_unique<Object>(); asynchrones_c_interface(object.release()); void thread_function(void* userData) { auto object = unique_ptr<Object>(static_cast<Object*>(userData)); // mach was mit object. }
-
Benutze std::thread oder boost::thread!