Verständnissfragen zu CreateThread _beginthread



  • 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...



  • Moin,

    vielen dank noch mal an euch alle, habt mir echt weitergeholfen!!!
    Das mit dem Makro klingt nicht schlecht (werd ich auf jeden fall mal austesten).
    Denk daß ich jetzt im groben durchblicke. Werde jetzt noch etwas mit den Casts und Calling Conventions herumspielen (nur verständnisshalber).
    ⚠ Nochmals Danke, und schönen Tag. ⚠

    Gruß Threaddy



  • Hi,

    hätte da noch eine Frage:
    Wie kann ich feststellen ob ein Thread noch läuft (nicht darauf warten wie mit WaitForSingleObject)?

    Danke im Voraus!

    Gruß Threaddy



  • if(WaitForSingleObject(hThread,0)==WAIT_TIMEOUT)
    { 
       // Thread läuft noch
    }
    else
    {
       // Thread läuft nimmer
    }
    

Anmelden zum Antworten