PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [C] pthread_create und die 1000 void sterne


Gast
2006-04-23, 17:58:09
Hi,

kann mir bitte jemand sagen, wie ich einen Thread erzeuge und dabei einen int-Parameter an die Funktion "im" Thread übergeben kann?


int machwas(int i) {
...
}
...
main() {
pthread_t myThread;
int i;
...
for (i=0; blah; i++) {
pthread_create( &myThread, NULL, (void *(*)(void *))machwas, (void *)i );
}
...


Ich muss zugeben, dass ich leider nicht weiss, was die ganzen voids und Sterne vor dem machwas im pthread_create bedeuten. Was void und Stern für sich gesehen bedeuten ist mir klar, aber in diesem Zusammenhang (pthread) und in dieser mir unverständlichen Anordnung verstehe ich gar nichts...

Stone2001
2006-04-23, 20:00:49
int machwas(int i) {
...
}
...
main() {
pthread_t myThread;
int i;
...
for (i=0; blah; i++) {
pthread_create( &myThread, NULL, (void *(*)(void *))machwas, (void *)i );
}
...

probier es mal hiermit:

void *machwas(int i) {
...
}
...
main() {
pthread_t myThread;
int i;
...
for (i=0; blah; i++) {
pthread_create( &myThread, NULL, machwas, (void *) i );
}
...


pthread_create erwartet eine Funktion mit einem void-Pointer als Rückgabewert und einen void-Pointer als Paramter.
Du mußt also deinen int-wert nach void* casten und dann in deiner Funktion ihn wieder zurück casten. Das mit dem Rückgabewert der Funktion ist nicht ganz so einfach, da gibt es spezielle Funktionen in der PThread-Bib. Da mußt du mal nachschauen. Ansonsten bleibt dir nur die Möglichkeit alles über globale Variablen zu machen, dann über gibt du keinen einzelnen int-Wert mehr, sonder einen kompletten Struct.

Monger
2006-04-23, 22:00:26
Was zur Hölle ist ein void Zeiger?!? Ein Zeiger ins Nichts?

Das ist genau der Grund weshalb ich C/C++ hasse: diese ganze Zeigerarithmetik ist eine Qual! :mad:

Coda
2006-04-23, 22:02:41
Was zur Hölle ist ein void Zeiger?!? Ein Zeiger ins Nichts?
Ein Zeiger auf einen beliebigen Datentyp.

Das ist genau der Grund weshalb ich C/C++ hasse: diese ganze Zeigerarithmetik ist eine Qual! :mad:
Eigentlich nicht wenn mans nen Weilchen gemacht hat und man lernt viel darüber wie der Rechner eigentlich funktioniert. Hat seine sehr positiven Seiten.

Es muss übrigens (void*)&i sein, weil du da ja die Speicheraddresse übergeben sollst.

Gast
2006-04-23, 22:28:58
Es muss übrigens (void*)&i sein, weil du da ja die Speicheraddresse übergeben sollst.Ja, habe ich durch Ausprobieren auch rausbekommen. Das Programm kompilierte dann zwar, aber das an 'machwas' übergebene i (Schleifenzähler) war dann in allen erzeugten Threads gleich dem maximal möglichen i. (in Schleife zB 4 Threads erzeugen, im Thread i ausgeben, 4x Ausgabe "i=4")

Ich habs nun so gemacht, dass in der Schleife zwar zB 4 Threads erzeugt werden, aber ihre eigene Nummer wird über eine globale Zählervariable realisiert:

static int threadzaehler = 0;

void* machwas() {
int meine_nummer = threadzaehler++;
printf("Ich bin Thread %d\n", meine_nummer);
...

Was dann die erwartete Ausgabe gibt:
Ich bin Thread 0
Ich bin Thread 1
Ich bin Thread 2
usw

SGT.Hawk
2006-04-25, 21:48:28
Ich galube *void kann man weglassen.

zeckensack
2006-04-26, 18:52:20
Hi,

kann mir bitte jemand sagen, wie ich einen Thread erzeuge und dabei einen int-Parameter an die Funktion "im" Thread übergeben kann?


int machwas(int i) {
...
}
...
main() {
pthread_t myThread;
int i;
...
for (i=0; blah; i++) {
pthread_create( &myThread, NULL, (void *(*)(void *))machwas, (void *)i );
}
...


Ich muss zugeben, dass ich leider nicht weiss, was die ganzen voids und Sterne vor dem machwas im pthread_create bedeuten. Was void und Stern für sich gesehen bedeuten ist mir klar, aber in diesem Zusammenhang (pthread) und in dieser mir unverständlichen Anordnung verstehe ich gar nichts...Die Funktion die den Startpunkt deines Threads definiert soll folgenden Typ haben:void* machwas(void* parameter);Btw erschütternd wie viele falsche Antworten hier schon kamen ... int als Rückgabetyp, jaja :rolleyes:
Bettlektüre (http://www.opengroup.org/onlinepubs/007908799/xsh/pthread_create.html
)

Das void mit den tausend Sternchen ist ein Cast des Zeigers auf deine Funktion auf den erwarteten Typen: ein Funktionszeiger auf eine Funktion die einen void* zurückgibt und ebenfalls einen void* als Parameter erwartet. Das ist nur deswegen notwendig, weil deine Funktion machwas den falschen Typ hat.

Der Cast könnte vereinfacht werden:typedef void* (*ThreadEntryFp)(void*);

int
main()
{
pthread_create(&myThread,0,(ThreadEntryFp)machwas,0);
}
Aber eigentlich ist das alles total falsch und immer noch ein schmutziger Hack, der dir evtl in Zukunft noch viele Kopfschmerzen bereiten wird. 64-Bit-System, anyone?
Also warum nicht gleich den richtigen Typ nehmen? Um den Cast loszuwerden empfiehlt sich als erste Maßnahme dieses:void*
machwas(void* parameter)
{
//...
}

int
main()
{
//...
pthread_create(&myThread,0,machwas,(void*)dein_lieblings_parameter);
}Kein Cast, keine Sternchen.

Nun zu dem Parameter.
Das ist nicht etwa deswegen ein void* um dich zu ärgern, sondern um die Funktion flexibel zu machen. Wenn es ein int wäre, könnte man dem Thread-Einsprungpunkt nur ein int übergeben. Bzw man müsste für jeden weiteren Datentypen eine neue Variante von pthread_create ersinnen.

Ein void* erlaubt dagegen die Übergabe von beliebigen structs, womit alle Flexibilitätsprobleme erledigt sind.

struct
ThreadInitParameter
{
int hausnummer;
bool kann_sprechen;
};

ThreadInitParameter tip1={1,true};
ThreadInitParameter tip2={2,false};
ThreadInitParameter tip3={3,true};

void*
thread_entry(void* param)
{
//cast nach ThreadInitParameter
ThreadInitParameter* _param=(ThreadInitParameter*)param;
if (_param->kann_sprechen) printf("Hallo, ich bin die Nummer %d.\n",_param->hausnummer);
else printf("Ich kann mich jetzt nicht äußern.\n");
return(0);
}

int
main()
{
//...
pthread_create(&worker1,0,thread_entry,&tip1);
pthread_create(&worker2,0,thread_entry,&tip2);
pthread_create(&worker3,0,thread_entry,&tip3);
//...
return(0);
}Es müssen auch keine globalen Variablen sein. Man kann sich selbstredend auch mit new (oder malloc) Speicher für die Startparameter holen, aber ja, da ein Zeiger übergeben wird müssen unterschiedliche Speicherbereiche genutzt werden, wenn du nicht so etwas erleben willst wie mit deinem ersten Versuch mit der Schleife, wo jeder Thread den gleichen Parameter bekam.
Hint: es gibt keine Garantie dass die Threads sofort anlaufen. Wenn der erste erst anläuft nachdem i bis 4 hochgezählt wurde, bekommt er eben auch einen Zeiger auf diese 4. Du hattest noch Glück dass da überhaupt eine Zahl aus einem halbwegs gültigen Wertebereich herauskam, es hätte auch sein können dass die Schleifenvariable in der Zwischenzeit längst vom Stack verschwunden und von irgendwelchem anderen Schrott überschrieben wurde.

Du möchtest erreichen dass der Wert den du für einen bestimmten Thread vorgesehen hast immer noch an gleicher Stelle und unverändert existiert wenn der Thread irgendwann mal anläuft. Da das eine unbestimmte Zeit ab dem Aufruf von pthread_create dauern kann, ist es am sichersten wenn du die Werte für jeden Thread getrennt und ~dauerhaft parkst, und "lange"(TM) nicht mehr antastest.

zeckensack
2006-04-26, 18:57:10
Es muss übrigens (void*)&i sein, weil du da ja die Speicheraddresse übergeben sollst.Bloß nicht.
Ein cast eines int zu einem void* ist noch halbwegs okay, da Zeiger immer mindestens soviele Bits haben wie int. Der cast ist damit zwar hässlich wie die Nacht, aber wenigstens verlustfrei.
Die Adresse durchzuschleusen ist ein recipe for desaster, wie Gast ja auch schon feststellen durfte.Ich galube *void kann man weglassen.Nein.

Monger
2006-04-26, 20:10:30
@Zeckensack:
Nur aus Interesse: kann ich den "void*" als Referenz auf ein beliebiges Objekt verstehen?

Ich kenn ja nunmal nur Java, und da ist "Object" quasi die Mutter aller Datentypen, Object geht als immer.

(Ganz richtig ist das nicht, weil eigentlich nur "null" in wirklich jedem Datentyp enthalten ist, aber das ist wahrscheinlich Korinthenkackerei)

Demirug
2006-04-26, 21:06:06
@Zeckensack:
Nur aus Interesse: kann ich den "void*" als Referenz auf ein beliebiges Objekt verstehen?

Ich kenn ja nunmal nur Java, und da ist "Object" quasi die Mutter aller Datentypen, Object geht als immer.

(Ganz richtig ist das nicht, weil eigentlich nur "null" in wirklich jedem Datentyp enthalten ist, aber das ist wahrscheinlich Korinthenkackerei)

Als Zeiger auf ein beliebiges Objekt. Zeiger und Referenzen sind in C/C++ nicht das selbe. Eine Referenz ist immer typisiert und verweisst auf ein gültiges Objekt. (OK das mit den gültigen Objekt kann man durch unvorsichtige Programmierung kaputt machen). Ein Zeiger dagegen kann auch auf nichts zeigen. Dafür gibt es die Konstante NULL. Zudem kann man Zeiger wild casten ohne das der Compiler meckert bei Referenzen geht das nicht.