Verständnissfragen zu CreateThread _beginthread



  • Hallo allerseits,

    hab mich in den letzten Tagen mal mit dem Thema Thread befassst, und jeden einzelnen Beitrag im Forum hier durchgelesen (zumindests kam's mir so vor). :p
    Hab mir dann aus verschiedenen Beiträgen mal ein lauffähiges Beispielprogramm des nix weiter macht als n bisschen Text auszugeben gecoded und hätte da noch n paar Verständnisfragen. Aber hier erst mal der Code:

    #include "stdafx.h"
    #include "windows.h" //für CreateThread
    #include "PROCESS.H" //für _beginthread()
    #include "conio.h"   //für kbhit()
    
    void threadFunc(int nr);
    
    main()
    {
    	HANDLE mythread[5];
    	for (int i=0; i<5; i++)
    	{
    		mythread[i]=CreateThread(NULL,256,(LPTHREAD_START_ROUTINE)threadFunc,(void*)i,NULL,NULL); //Frage 1+2
    		//_beginthread(threadFunc,0,NULL); 
    		printf("Handle[%i]=%d\n",i,mythread[i]);
    	}
    	printf("\n");
    	while(!kbhit()) //beendet die Schleife sobald eine Taste gedrückt wird
    	{
    		printf("Thread main\n");
    		Sleep(700);
    	}
    	printf("*** Thread main beendet ***\n");
    	for (i=0; i<5; i++)
    		WaitForSingleObject(mythread[i], INFINITE); //Wartet unendlich bis der Thread beendet ist
    	printf("*** Programm beendet ***\n");
    	Sleep(5000);
    }
    
    void threadFunc(int nr)
    {
    	for (int i=0; i<20; i++)
    	{
    		printf("Thread %i\tDurchlauf: %i\n",nr,i+1);
    		Sleep(1000+(nr*100)); //Damit alles etwas realistischer wird
    	}
    	printf("*** Thread %i beendet ***\n",nr);
    }
    

    Frage 1: Für was ist dieses (LPTHREAD_START_ROUTINE) vor der Threadfunction gut? Sieht aus wie'n Cast (für mich zumindest). Es gibt aber auch massenhaft Beiträge hier im Forum, in dem das weggelassen wurde, und scheint troztdem zu funktionieren. Aus der MSDN bin ich auch net schlauer geworden.

    Frage 2: Den Cast (*void) vor dem Parameter i in der For-Schleife hab ich hinzugefügt weil mein Compiler gemeckert hat daß die Typen inkompatiebel sind (trial and error Verfahren). Wie ich einen int-Wert auf void (noch dazu als Zeiger) casten kann oder was das soll weiß ich auch nicht.

    Frage 3: Hab das ganze dann auch mal in 'ne DLL gpackt und geschaut ob er's so Compiliert. Hat funktioniert 🤡 (hat mich selber überrascht). Gibt's da irgendwas zu beachten (außer bei noch laufendem DLL-Thread Hauptprogramm beenden)

    Frage 4: Ach ja, hätt ich beinahe vergessen: Warum geht des bei mir eigentlich mit _beginthread(threadFunc,0,NULL) nicht? Hab jetzt die Compilerfehlermeldung nicht mehr da, glaub aber waren auch unvereinbare Typen dabei. Was ist eigentlich der Unterschied zwischen den beiden Methoden einen Thread zu starten (welche ist die "bessere")?

    Danke schon mal im voraus!!!
    Und schönen Abend noch!



  • 1. Jo, ist ein cast. Wenn calling convention (__stdcall, __cdecl, ...) und functionksdeklaration passen solltest den eingelich garn net brauchen (was bei dir net der fall ist, da deine funktion ein int leine LPDWORD als parameter erwartet).
    LPTHREAD_START_ROUTINE:
    typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE;
    PTHREAD_START_ROUTINE LPTHREAD:
    typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)(LPVOID lpThreadParameter);

    2. Warum void*??? CreateThread erwater doch nen LPDWORD (Zeiger auf eine DWORD)

    3. nö, eigenlich nichts was nicht auch bei single-thread und dlls zu beachten wär.

    4. Schätze mal es lag an der Funktions-deklaration. _beginthread will einen void* als parameter in deiner funktion kein int.
    Der Unterschied: CreateThread ist ne WinAPI funktion, _beginthread ne CRT (C-Runtime) funktion. Der für dich wichtige unterschied: CreateThread aus nem c++ prog aufgerufen hinterlässt ein speicherleck, _beginthread nicht.



  • Hi,
    danke erst mal für deine schnelle Antwort!!!
    Bin noch Frischling in C++ und hab da noch n paar (vielleicht dumme) Fragen (ich bitte das zu entschuldigen):

    1. Was hat das genau mit der Calling convention auf sich? Hab im Forum/Google gesucht und herausgefunden daß das was mit der Art der Parameterübergabe über den Stack zu tun hat. Aber so richtig schlau werd ich daraus net, wie kann man mit einem Cast die Parameterübergabe beinflussen? Hab irgendwo gelesen daß Methoden mit verschiedenen Calling Conventions zueinander inkompatiebel sind. Wenn's dazu irgendwo 'ne vernünftige Erklärung gibt bitte her damit. ⚠

    2. Warum void*: Weil mein Compiler das so haben wollte ;)(funktioniert ja auch so). Aber erst mal ganz dumm gefragt: void heißt doch normalerweise so viel wie "gar nix". Bei Rückgabewerten seh ich ja ein daß man das als Indikator für nix (also kein Rückgabewert der Funktion) void schreibt. Aber wie kann ich einer Funktion einen Datentyp void übergeben (heißt das dann da was andres als nix???). Und was zur hölle ist void*? (n Zeiger auf nix? 😕 )

    3. Des war genau die Antwort die ich auf die Frage haben wollte! Danke.

    4. Über das void* als Parameter hab ich mich ja schon bei Punkt 2 gewundert. Speicherleck is doch schlecht, warum gibt's die Funktion dann eigentlich? Ist diese Funktion nur für andre Sprachen 'zu gebrauchen'? Werde mit dem _beginthread noch etwas experimentieren.

    Ach so: gibt's da net irgendwie auch 'n System was die Groß-kleinschreibung betrifft: Sleep, printf, WaitForSingleObject, _beginthread 😕
    Ich kenn das so: Methodennamen klein, wenn aus mehreren Wörtern zusammengesetzt dann zweites Wort groß schreiben (z.B. waitForSingleObject)

    Also nochmals tschuldigung für die vielen Fragen (wäre aber doch nett wenn mir das mal jemand verständlich machen könnte). Wäre auch für 'ne Buchempfehlung zu haben. Das C++ Tutorial der UniMarburg schweigt sich zum Thema Threads leider etwas aus (den Rest find ich aber echt super gemacht!)

    Für alle Antworten danke im Voraus!
    special Thx 2 CMatt!

    Gruß Threaddy



  • 1. Jo, das ist die Art der parameterübergabe. Ein Pascal prog pusht die parameter z.B. genau umgekehrt als ein c prog auf den stack. Durch nen cast kannst du das nicht beeinflussen, aber durch richtige deklaration 🤡

    2. void* -> ist ein zeiger auf nen beliebige datentypen. Du kannst nen long*, short*, CIrgendwas*, ... übergen ohne zu casten.

    3. CreateThread ist WinAPI und WinAPI soll ja mit mehreren Sprachen funzen net nur C/C++. Der Grund für das memory leak, in C/C++ gibts ne menge globaler variablen die von der CRT verwendet werden (wie errno). So ein Variablenblock existiert für jeden Thread. CreateThread hat aber keine blassen schimmer aus welchem Prog es aufgerufen wird und macht deshalb erst mal gar nix (ausser nen Thread zu starten). Sobald dein code jetzt so ne variable verwenden möchte muss dieser Variable-Block angelegt werden. Und wer kümmert sich jetzt drum, das der block wieder freigeben wird wenn der Thread beended wird? Keiner -> memoryleak. _beginthread legt nen block gleich beim threadstart mit an und kümmert sich darum, dass das er auch wieder freigeben wird wenn fertig ist.

    Nö, nichts fest definiertes.

    Zu den büchern:
    Windows-Programmierung | ISBN: 3860634879
    Die Bibel für WinAPI Einsteiger

    Microsoft Windows Programmierung für Experten | ISBN: 3860636154
    In dem Ding werden dir Threads, Prozesse und Dlls auf 1050 Seiten so lage eingetrichtert bis du automatisch SuspendThread vorm einschalfen und ResumeThread bei aufstehen schreist.

    *Sollest du eines der Bücher bestellen, bitte machs über link oben 🤡



  • Hi,
    nochmals Danke!

    Aber mit dem Cast LPTHREAD_START_ROUTINE geht's dann doch irgendwie, also muß der doch irgenwas bewirken. Wie die Parameterliste auf dem Stack liegt kann mir doch egal sein (solange ich nur C-Routinen aufruf). Is das dann net nur wichtig wenn ich z.B. Pascal-Progs aufrufe?

    OK so leuchtet mir das void* ein. Dann kann ich meine Funktion variabler halten, muß halt dann in der Funktion dafür sorgen, daß er's richtig behandelt bzw. richtig Castet.

    OK leuchtet mir auch ein 💡

    Danke!

    Gruß Threaddy



  • Aber mit dem Cast LPTHREAD_START_ROUTINE geht's dann doch irgendwie, also muß der doch irgenwas bewirken.

    char c;
    unsigned __int64 *i = (unsigned __int64*)&c;
    *i = -1;
    

    ^^ auch das geflecht firsst ein compiler ohne probleme... das ist ihm doch egal ob dein prog abschmiert... C-Cast in compiler sprache: "mach du ma, ich weis schon was ich da tue". Du solltest auf jeden fall die richtige calling convention verwenden (gibt auch in c++ verschiedene (__cdecl, __stdcall, __fastcall,..). Ne falsche in die richtige gecastet kann durchaus auch in nem reinen C++ prog ins Auge gehen. Sonst kommen da plötzlich so "nichtssagende" Meldungen wie "corrupt ESP register" 😉



  • Hi,

    hab aber schon viele Programme gesehn die des so realisieren. Is des so verbreitet? Muß doch mal zusehen daß ich das irgendwie ohne Cast hin kriege. Aber mal ganz frech gefragt: Was muß ich denn in meinem Code ändern damit die Calling Convention passt? Einfach __cdecl vor die Methode?
    Aber mal was andres:
    Hab mal versucht das ganze mit _beginthread (bin ich da jetzt im falschen Forum?) zu machen:

    void threadFunc(void* nr);
    
    void main()
    {
    	HANDLE mythread[5];
    	for (int i=0; i<5; i++)
    	{
    		//mythread[i]=CreateThread(NULL,256,(LPTHREAD_START_ROUTINE)threadFunc,(void*)i,NULL,NULL);
    		mythread[i]=_beginthread(threadFunc,0,NULL); 
    		printf("Handle[%i]=%d\n",i,mythread[i]);
    	}
    	printf("\n");
    	while(!kbhit()) //beendet die Schleife sobald eine Taste gedrückt wird
    	{
    		printf("Thread main\n");
    		Sleep(700);
    	}
    	printf("*** Thread main beendet ***\n");
    	for (i=0; i<5; i++)
    		WaitForSingleObject(mythread[i], INFINITE);
    	printf("*** Programm beendet ***\n");
    	Sleep(5000);
    }
    
    void threadFunc(void* value)
    {
    	int nr=(int) value; //Cast des beliebigen Typs auf int
    	for (int i=0; i<20; i++)
    	{
    		printf("Thread %i\tDurchlauf: %i\n",nr,i+1);
    		Sleep(1000+(nr*100)); //Damit alles etwas realistischer wird
    	}
    	printf("*** Thread %i beendet ***\n",nr);
    }
    

    Hat soweit funktioniert, außer daß value an threadFunc immer 0 übergeben wurde (aber is ja auch klar, hab ja keinen Parameter angegeben), aber er hat zumindest mal 5 Threads gestartet. Ich hab dann versucht einfach die Variable i (aus der Schleife) zu übergeben:

    mythread[i]=_beginthread(threadFunc(i),0,NULL);
    

    Hat folgende Fehlermeldung gebracht:

    threadtest2.cpp(14) : error C2664: 'threadFunc' : Konvertierung des Parameters 1 von 'int' in 'void *' nicht moeglich
    Die Konvertierung eines ganzzahligen Typs in einen Zeigertyp erfordert ein reinterpret_cast-Operator oder eine Typumwandlung im C- oder Funktionsformat

    Is ja logisch dacht ich mir und hab dann folgendes probiert:

    mythread[i]=_beginthread(threadFunc((void*)i),0,NULL);
    

    Gab dann diese Fehlermeldung:

    threadtest2.cpp(14) : error C2664: '_beginthread' : Konvertierung des Parameters 1 von 'void' in 'void (__cdecl *)(void *)' nicht moeglich
    Ausdruck vom Typ void kann nicht in andere Typen konvertiert werden

    Das mit dem reinterpret_cast das der Compiler vorgeschlagen hat, hat auch nicht funktioniert.
    Wie krieg ich jetzt der Methode den Wert von i übergeben (Evtl. auch mit richtiger Calling Convention)?
    Kann man einer Methode einfach void* übergeben und in der Methode dan casten oder is das untypisch (oder schlecht)?
    Kann ich eigentlich feststellen was für'n Datentyp das void* jetzt wirklich beeinhaltet (befürchte fast nicht)? Könnte ja irgendwer auch char[] übergeben haben, dann interpretiert der das Bitmuster des Characters doch einfach als int (ich glaub des wäre schlecht).

    Wie immer danke im voraus!!!!!!!

    Gruß Threaddy



  • _beginthread gibt keinen Handle auf den Thread zurück!

    Das tut nur _beginthreadex!

    Du kannst also keinen Thread mit _beginthread abspalten und dann mit WaitFor... auf seine Beendigung warten! Um das tun zu können müßtest Du _beginthreadex benutzen...



  • Ohps,
    danke für die Info ⚠ Muß ich gleich noch mal Testen. Ich denk aber daß es funktioniert hat: Wenn ich die Schleife in main() vor den Threads beende kommt die Ausgabe '*** Programm beendet ***' auch erst ganz am Schluß. Auch das printf gibt mir unterschiedliche Werte für die Handles an.
    Teste das gleich noch mal und sag bescheid...

    Danke Gruß Threaddy.



  • Threaddy schrieb:

    Hat soweit funktioniert, außer daß value an threadFunc immer 0 übergeben wurde (aber is ja auch klar, hab ja keinen Parameter angegeben), aber er hat zumindest mal 5 Threads gestartet. Ich hab dann versucht einfach die Variable i (aus der Schleife) zu übergeben:

    ...

    Wie krieg ich jetzt der Methode den Wert von i übergeben (Evtl. auch mit richtiger Calling Convention)?

    Deine threadFunc erwartet als Parameter eine Adresse auf einen beliebigen Typ...

    _beginthread(threadFunc, 0, (void *) i);
    

    Kann ich eigentlich feststellen was für'n Datentyp das void* jetzt wirklich beeinhaltet (befürchte fast nicht)? Könnte ja irgendwer auch char[] übergeben haben, dann interpretiert der das Bitmuster des Characters doch einfach als int (ich glaub des wäre schlecht).

    Du übergibst doch den Wert an die ThreadFunc, also weißt Du doch genau, welcher Typ das ist...

    Wenn Du unterscheiden willst, brauchst Du halt mehrere Thread-Funktionen, die Du bei Bedarf halt angibst...

    Ein Thread benutzt die Funktion, die Du als ersten Parameter an _beginthread übergibst...

    Lies Dir am Besten mal die Doku zu _beginthread/_beginthreadex in der MSDN durch...



  • Hi,

    hab schnell folgendes Improvisiert:

    void threadFunc(void* nr);
    
    static int nr;
    
    void main()
    {
    	nr=0;
    	HANDLE mythread[5];
    	for (int i=0; i<5; i++)
    	{
    		//mythread[i]=CreateThread(NULL,256,(LPTHREAD_START_ROUTINE)threadFunc,(void*)i,NULL,NULL);
    		mythread[i]=NULL;
    		mythread[i]=(void*)_beginthread(threadFunc,0,NULL); 
    		printf("Handle[%i]=%d\n",i,mythread[i]);
    		Sleep(100);
    		nr++;
    	}
    	printf("\n");
    	while(!kbhit()) //beendet die Schleife sobald eine Taste gedrückt wird
    	{
    		printf("Thread main\n");
    		Sleep(700);
    	}
    	printf("*** Thread main beendet ***\n");
    	for (i=0; i<5; i++)
    		WaitForSingleObject(mythread[i], INFINITE);
    	printf("*** Programm beendet ***\n");
    	Sleep(5000);
    }
    
    void threadFunc(void* value)
    {
    	int number=nr;
    	//int nr=(int) value; //Cast des beliebigen Typs auf int
    	for (int i=0; i<20; i++)
    	{
    		printf("Thread %i\tDurchlauf: %i\n",number,i+1);
    		Sleep(1000+(nr*100)); //Damit alles etwas realistischer wird
    	}
    	printf("*** Thread %i beendet ***\n",number);
    }
    

    Hat folgendes Ergebnis gebracht:
    Handle[0]=2024 <=== ist auf jeden Fall nicht mehr NULL
    Thread 0 Durchlauf: 1
    Handle[1]=2036
    Thread 1 Durchlauf: 1
    Handle[2]=2012
    Thread 2 Durchlauf: 1
    Handle[3]=2008
    Thread 3 Durchlauf: 1
    Handle[4]=2004
    Thread 4 Durchlauf: 1

    Thread main
    Thread 0 Durchlauf: 2
    Thread 1 Durchlauf: 2
    Thread main
    Thread 2 Durchlauf: 2
    Thread 3 Durchlauf: 2
    Thread 4 Durchlauf: 2
    Thread main
    Thread 0 Durchlauf: 3
    Thread main
    Thread 1 Durchlauf: 3
    Thread 2 Durchlauf: 3
    Thread 3 Durchlauf: 3
    Thread 4 Durchlauf: 3
    Thread main
    Thread 0 Durchlauf: 4
    Thread main
    Thread 1 Durchlauf: 4
    Thread 2 Durchlauf: 4
    Thread 3 Durchlauf: 4
    *** Thread main beendet *** <==== Schleife in main()beendet
    Thread 4 Durchlauf: 4
    Thread 0 Durchlauf: 5
    Thread 1 Durchlauf: 5
    Thread 2 Durchlauf: 5
    Thread 3 Durchlauf: 5
    Thread 4 Durchlauf: 5
    Thread 0 Durchlauf: 6
    Thread 1 Durchlauf: 6
    Thread 2 Durchlauf: 6
    Thread 3 Durchlauf: 6
    Thread 4 Durchlauf: 6
    Thread 0 Durchlauf: 7
    Thread 1 Durchlauf: 7
    Thread 2 Durchlauf: 7
    Thread 3 Durchlauf: 7
    Thread 4 Durchlauf: 7
    Thread 0 Durchlauf: 8
    Thread 1 Durchlauf: 8
    Thread 2 Durchlauf: 8
    Thread 3 Durchlauf: 8
    Thread 4 Durchlauf: 8
    Thread 0 Durchlauf: 9
    Thread 1 Durchlauf: 9
    Thread 2 Durchlauf: 9
    Thread 3 Durchlauf: 9
    Thread 4 Durchlauf: 9
    Thread 0 Durchlauf: 10
    Thread 1 Durchlauf: 10
    Thread 2 Durchlauf: 10
    Thread 3 Durchlauf: 10
    Thread 4 Durchlauf: 10
    Thread 0 Durchlauf: 11
    Thread 1 Durchlauf: 11
    Thread 2 Durchlauf: 11
    Thread 3 Durchlauf: 11
    Thread 4 Durchlauf: 11
    Thread 0 Durchlauf: 12
    Thread 1 Durchlauf: 12
    Thread 2 Durchlauf: 12
    Thread 3 Durchlauf: 12
    Thread 4 Durchlauf: 12
    Thread 0 Durchlauf: 13
    Thread 1 Durchlauf: 13
    Thread 2 Durchlauf: 13
    Thread 3 Durchlauf: 13
    Thread 4 Durchlauf: 13
    Thread 0 Durchlauf: 14
    Thread 1 Durchlauf: 14
    Thread 2 Durchlauf: 14
    Thread 3 Durchlauf: 14
    Thread 4 Durchlauf: 14
    Thread 0 Durchlauf: 15
    Thread 1 Durchlauf: 15
    Thread 2 Durchlauf: 15
    Thread 3 Durchlauf: 15
    Thread 4 Durchlauf: 15
    Thread 0 Durchlauf: 16
    Thread 1 Durchlauf: 16
    Thread 2 Durchlauf: 16
    Thread 3 Durchlauf: 16
    Thread 4 Durchlauf: 16
    Thread 0 Durchlauf: 17
    Thread 1 Durchlauf: 17
    Thread 2 Durchlauf: 17
    Thread 3 Durchlauf: 17
    Thread 4 Durchlauf: 17
    Thread 0 Durchlauf: 18
    Thread 1 Durchlauf: 18
    Thread 2 Durchlauf: 18
    Thread 3 Durchlauf: 18
    Thread 4 Durchlauf: 18
    Thread 0 Durchlauf: 19
    Thread 1 Durchlauf: 19
    Thread 2 Durchlauf: 19
    Thread 3 Durchlauf: 19
    Thread 4 Durchlauf: 19
    Thread 0 Durchlauf: 20
    Thread 1 Durchlauf: 20
    Thread 2 Durchlauf: 20
    Thread 3 Durchlauf: 20
    Thread 4 Durchlauf: 20
    *** Thread 0 beendet ***
    *** Thread 1 beendet ***
    *** Thread 2 beendet ***
    *** Thread 3 beendet ***
    *** Thread 4 beendet ***
    *** Programm beendet *** <=== Diese ausgabe steht in main direkt nach WaitFor..

    -->Glaube schon daß ich da'n Handle zurück kriege (schau gleich noch mal bei MSDN)



  • MSDN schrieb:

    The _beginthreadex function gives you more control over how the thread is created than _beginthread does. The _endthreadex function is also more flexible. For example, with _beginthreadex, you can use security information, set the initial state of the thread (running or suspended), and get the thread identifier of the newly created thread. You are also able to use the thread handle returned by _beginthreadex with the synchronization APIs, which you cannot do with _beginthread.



  • _beginthread(threadFunc, 0, (void 😉 i);

    Stimmt, wer lesen kann is klar im Vorteil!!!
    Man sollte schon an der richtigen Stelle den Parameter übergeben 🙄

    Danke! Is wohl doch schon zu spät!
    Was sagst du zu dem Handle? Irgendwie scheint's zu gehn.

    Gruß Threaddy!



  • BTW, Du solltest im übrigen die Handles alle wieder schließen nach Benutzung...

    CloseHandle[i];
    

    Und es ist egal, was ich zu den Handles sage, entscheident ist die MSDN, und die hab ich oben bereits zitiert...



  • Ok,

    dann woll'n wer uns mal nicht über die MSDN hinwegsetzten, mal schaun ob's dann irgendwie anders aussieht. War jetzt nur neugierig (wenn kein Handle zurückkommt müsst die Ausgabe doch anders aussehen) 😕

    Gruß und Schönen Abend noch
    Threaddy



  • Sicherlich liefert _beginthread einen Wert zurück, ohne Frage.

    Du kannst diesen Handle aber nicht mit den Funktionen aus der Syncronisations-API benutzen...

    Werte doch mal den Rückgabewert von WaitForSingleObject aus...

    MSDN schrieb:

    If the function succeeds, the return value indicates the event that caused the function to return. It can be one of the following values.

    Return code Description
    WAIT_ABANDONED
    The specified object is a mutex object that was not released by the thread that owned the mutex object before the owning thread terminated. Ownership of the mutex object is granted to the calling thread, and the mutex is set to nonsignaled.

    WAIT_OBJECT_0
    The state of the specified object is signaled.

    WAIT_TIMEOUT
    The time-out interval elapsed, and the object's state is nonsignaled.

    If the function fails, the return value is WAIT_FAILED. To get extended error information, call GetLastError.



  • [EDIT] käse 🤡 [/EDIT]



  • Hinterlass hier bitte nicht deine Füße. 😉



  • Hi,

    CloseHandle[i];

    OkOk 'Invalid Handle' bei 'CloseHandle(mythread[i])' (wohl ehr so) überzeugt mich dann doch! Wollte ja keinem widersprechen, wollt's halt nur wissen (bin neugierig).
    Das ganze sieht jetzt so aus:

    unsigned __stdcall threadFunc(void* nr);
    
    void main()
    {
    	HANDLE mythread[5];
    	for (int i=0; i<5; i++)
    	{
    		//mythread[i]=CreateThread(NULL,256,(LPTHREAD_START_ROUTINE)threadFunc,(void*)i,NULL,NULL);
    		mythread[i]=NULL;
    		mythread[i]=(HANDLE)_beginthreadex(NULL,0,&threadFunc,(void*)i,0,NULL); 
    		printf("Handle[%i]=%d\n",i,mythread[i]);
    	}//
    	printf("\n");
    	while(!kbhit()) //beendet die Schleife sobald eine Taste gedrückt wird
    	{
    		printf("Thread main\n");
    		Sleep(700);
    	}
    	printf("*** Thread main beendet ***\n");
    	for (i=0; i<5; i++)
    	{	WaitForSingleObject(mythread[i], INFINITE);
    		CloseHandle(mythread[i]);
    	}
    	printf("*** Programm beendet ***\n");
    	Sleep(5000);
    }
    
    unsigned __stdcall threadFunc(void* value)
    {
    	int nr=(int) value; //Cast des beliebigen Typs auf int
    	for (int i=0; i<20; i++)
    	{
    		printf("Thread %i\tDurchlauf: %i\n",nr,i+1);
    		Sleep(1000+(nr*100)); //Damit alles etwas realistischer wird
    	}
    	printf("*** Thread %i beendet ***\n",nr);
    	return 0;
    }
    

    hab mir den Rückgabetyp (unsigned __stdcall) aus der MSDN geklaut. Wenn jemand noch Zeit (und Geduld) hat kann er ja noch mal schreiben ob das jetzt der Calling Convention entspricht.
    Danke auf jeden Fall schon mal an alle, hat mich schon mal weiter gebracht.
    Mach jetzt erst mal Feierabend (denk mir das morgen noch mal durch).

    Gruß Threaddy



  • Jo paßt.

    Wenn Dir die ganze casterei zuviel wird, in dem Buch "Microsoft Windows Programmierung für Experten" (Link siehe oben von CMatt gepostet) bekommt man ein Makro, welches die Casterei übernimmt.

    typedef unsigned(__stdcall *PTHREAD_START) (void *);
    
    #define MyBeginThreadEx(psa, cbStack, pfnStartAddr, pvParam, fdwCreate, pdwThreadId) \
    	((HANDLE)_beginthreadex((void *)(psa),(unsigned)(cbStack),(PTHREAD_START)(pfnStartAddr),(void *)(pvParam),(unsigned)(fdwCreate),(unsigned *)(pdwThreadId)))
    

    So startest Du dann einen Thread:

    hThreadHandle = MyBeginThreadEx(NULL, 0, Thread_Funktion, Parameter, NULL, NULL);
    

    Die Deklaration der ThreadFunktion sieht dann so aus:

    DWORD WINAPI Thread_Funktion(void *Parameter);
    

    Somit brauchst Du Dir dann um das casten keinen Kopp mehr machen...


Anmelden zum Antworten