Guten Morgen, ich möchte einen Thread in eine eigene Klasse unterbringen. Danach soll diese von einer anderen Klasse zugriff besitzen. WIe kann ich in C++ einen Thread in eine eigene Klasse unterbringen? Hat jemand das schon einmal gemacht?
Ich hab mal jetzt eine Klasse Thread erstellt. Nun erscheint immer folgende Fehlermeldung: >>'CreateThread' : Konvertierung des Parameters 3 von 'unsigned long' in >>'unsigned long (__stdcall *)(void *)' nicht moeglich >>Keine Funktion mit diesem Namen im Gueltigkeitsbereich stimmt mit dem >>Zieltyp ueberein
1 | class Thread |
2 | {
|
3 | private:
|
4 | |
5 | HANDLE hThread; |
6 | DWORD dwThreadID; |
7 | LPVOID pParam; |
8 | |
9 | enum Status |
10 | {
|
11 | STOPPED, |
12 | RUNNING, |
13 | SUSPENDING
|
14 | };
|
15 | |
16 | int status; |
17 | |
18 | public:
|
19 | |
20 | void start (); |
21 | void suspend (); |
22 | void stop (); |
23 | |
24 | void setParameter(LPVOID pParam); |
25 | |
26 | virtual DWORD WINAPI my_Thread(void* unused); |
27 | |
28 | Thread(); |
29 | ~Thread(); |
30 | };
|
Datei Thread.cpp
1 | #include "thread.h" |
2 | |
3 | void Thread::start() |
4 | {
|
5 | if (status == RUNNING) |
6 | {
|
7 | // Thread is running
|
8 | return; |
9 | }
|
10 | else if (status == SUSPENDING) |
11 | {
|
12 | // Thread continue
|
13 | status = RUNNING; |
14 | ResumeThread(hThread); |
15 | }
|
16 | else
|
17 | {
|
18 | // Thread start
|
19 | status = RUNNING; |
20 | hThread = CreateThread(NULL, Thread_STACK_SIZE, my_Thread, 0, 0, &dwThreadID); |
21 | }
|
22 | }
|
23 | |
24 | DWORD WINAPI Thread::my_Thread(void* unused) |
25 | {
|
26 | while (true) |
27 | {
|
28 | //Sleep(1000);
|
29 | }
|
30 | return (0); |
31 | }
|
Das verstehe ich nicht. Was mache ich falsch?
Die Threadfunktion my_thread muss statisch sein, wie bei jeder anderen Anwendung auch, bei der Callback-Funktionen verwendet werden. Was erst recht nicht geht ist eine virtuelle Funktion. Den this-Pointer Deines Objektes muss Du von Hand mitliefern und in der my_thread-Funktion explizit auswerten. Allerdings spricht nichts dagegen, dann unmittelbar eine andere Funktion des Objektes aus dieser statischen Threadfunktion heraus aufzurufen, und die mag dann auch gerne virtuell sein. Also:
1 | class Thread |
2 | {
|
3 | private:
|
4 | |
5 | HANDLE hThread; |
6 | DWORD dwThreadID; |
7 | LPVOID pParam; |
8 | |
9 | enum Status |
10 | {
|
11 | STOPPED, |
12 | RUNNING, |
13 | SUSPENDING
|
14 | };
|
15 | |
16 | int status; |
17 | |
18 | public:
|
19 | |
20 | void start (); |
21 | void suspend (); |
22 | void stop (); |
23 | |
24 | void setParameter(LPVOID pParam); |
25 | |
26 | |
27 | virtual DWORD ThreadFunction(void); |
28 | |
29 | |
30 | Thread(); |
31 | ~Thread(); |
32 | |
33 | protected:
|
34 | static DWORD WINAPI StaticThreadFunction(void* pthis); |
35 | };
|
und entsprechend :
1 | #include "thread.h" |
2 | |
3 | void Thread::start() |
4 | {
|
5 | if (status == RUNNING) |
6 | {
|
7 | // Thread is running
|
8 | return; |
9 | }
|
10 | else if (status == SUSPENDING) |
11 | {
|
12 | // Thread continue
|
13 | status = RUNNING; |
14 | ResumeThread(hThread); |
15 | }
|
16 | else
|
17 | {
|
18 | // Thread start
|
19 | status = RUNNING; |
20 | hThread = CreateThread(NULL, Thread_STACK_SIZE, StaticThreadFunction, this, 0, &dwThreadID); |
21 | }
|
22 | }
|
23 | |
24 | DWORD WINAPI Thread::StaticThreadFunction(void* pthis) |
25 | {
|
26 | Thread *pThis; |
27 | |
28 | pThis = (Thread *) pthis; |
29 | // jaaa, ich weiß. Das ist ein C-Typecast.
|
30 | |
31 | return pThis->ThreadFunction(); |
32 | |
33 | }
|
34 | |
35 | |
36 | DWORD Thread::ThreadFunction(void) |
37 | {
|
38 | while (true) |
39 | {
|
40 | //Sleep(1000);
|
41 | }
|
42 | return (0); |
43 | }
|
Ob Deine "status"-Auswertung so irgendeinen Sinn hat, will ich jetzt nicht weiter untersuchen. Du könntest einfach in der statischen Threadfunktion ein Flag setzen, bevor die nichtstatische Threadfunktion aufgerufen wird und dieses wieder löschen, wenn diese zurückkehrt, aber das wertet naturgemäß den suspend-Zustand nicht aus. Braucht man das?
Hallo Rufus, danke für die Unterstützung. Leider klappt habe ich jetzt ein anderes Problem. Der Thread wird überhaupt nicht gestartet. Ich habe das ganze jetzt mal soweit reduziert, dass ich nur die Thread Klasse benutze. Das heisst ohne irgendwelche Ableitung usw. Was mache ich da falsch. Der Thread läuft überhaupt nicht.
Nur als Bemerkung: CreateThread solltest du besser nicht benutzen, wenn du C++ programmierst. Denn CreateThread ist eine WinAPI-Funktion, die C++-runtime-library bekommt davon nichts mit. Das muss sie aber, um bestimmte thread-lokale Variablen zu initalisieren. Aus dem Grund gibt es _beginthread und _beginthreadex. Aus ähnlichen, aber schwerwiegenderen Gründen ist TerminateThread in C++ absolut tabu (abgesehen von ganz seltenen Ausnahmefällen). Stichpunkt Destruktoren und belegte Resourcen, das kann ganz schnell zu Deadlocks führen. Sowas löst man üblicherweise über Events. Zu deiner Thread-Funktion: Den Parameter "unused" zu nennen ist ein wenig irreführend, da er offensichtlich nicht "unused" ist. Das ist übrigens auch dein Fehler: Du machst irgendwas mit dem Parameter der Thread-Funktion, gibst aber _beginthread eine 0 für diesen Parameter (wieso _beginthread und nicht CreateThread s.o.). Außerdem übergibst du _beginthread einen Zeiger auf my_Thread. Das ist aber nicht erlaubt ohne ein führendes & um die Adresse der Funktion zu bekommen (das ist einer der Unterschiede zwischen C und C++). Das du dafür keinen Fehler oder Warnung bekommst, spricht möglicherweise für einen veralteten Compiler.
Ich verwende Microsoft Visual Studio C++.NET 2003. Kannst du mir einen Beispielcode geben?
Ok eins habe ich noch vergessen zu erwähnen. Diese Klasse Thread habe ich auf meinem Heim-PC mit Microsoft Visual Studio C++ versucht zum laufen zu bringen. Tut sich nichts! In der Firma habe ich einen Embedded-PC (32 Bit) und da drauf läuft ein Echtzeitbetriebssystem. Dieses Programmiere ich mit Microsoft Visual 6.0 C++. Auf diesem System gibt es noch andere Thread die laufen. Diese wurden auch mit createThread erzeugt und funktioniert. Allerdings habe ich diese Threads nicht programmiert. Fakt ist ich habe meinen Thread schon mal auf meinem Embedded-PC am laufen gehapt. Ich weiss nicht mehr weiter.Nur für testzwecke habe ich mir die Klasse Thread kopiert und auf meinem heim-PC ausprobiert. Also CreateThread müsste gehen! Aber weiss leider nicht mehr weiter!
> Also CreateThread müsste gehen!
Ja, CreateThread "geht" schon; es wird ein neuer Thread erzeugt, der
auch einwandfrei laufen kann. Probleme werden aber auftreten, sobald du
bestimmte Funktionen der C-Runtime-Lib aufrufst. Im Prinzip sind für
einen mit CreateThread erzeugten Thread alle Standard-Funktionen
verboten, die intern über einen globalen Status verfügen.
Andernfalls würden verschiedene Threads dieselben, unsynchronisierten
Status-Variablen benutzen, was zwangsläufig zu Problemen führen wird;
nicht unbedingt Crashs, aber sehr schwer zu findende Bugs, sogenannte
Heisenbugs.
Hast du mal mit einem Debugger geschaut, ob der Thread tatsächlich nicht
anläuft?
Wie gesagt: Dein Fehler im letzten Code ist, dass die Thread-Funktion
einen Parameter bekommt, der ganz und gar nicht "unused" ist. Deswegen
musst du diesen Parameter natürlich auch beim _beginthread-Aufruf
bereitstellen. Du möchtest dort anscheinend den this-Zeiger übergeben.
Die Anzahl der C-Runtime-Routinen mit internem Statuscode ist glücklicherweise überschaubar, in den etwa 14 Jahren, in denen ich mit Threads unter Win32 arbeite (ja, seit einer Beta-Version von NT3.1) hat der Gebrauch von CreateThread mir noch keine Probleme bereitet. Allerdings gebrauche ich C-Runtime-Routinen eher konservativ, sowas wie strtok fasse ich nicht an. "Erfreulicherweise" schreibt auch die MS-Dokumentation zu CreateThread/_beginthread im wesentlichen nur das, was Chris angeführt hat, jedoch wird nicht erwähnt, welche Runtime-Routinen überhaupt interne Statuscodes verwenden. Ganz wichtig aber ist, daß man mit der richtigen Ausgabe der C-Runtime-Library linkt, da ist statt "singlethreaded" "multithreaded" zu wählen. @X_Rider: Ich habe die Variablennamen in meinem Beispiel sehr bewusst gewählt; Deine "unused"-Spielerei verschleiert das Problem, wie Chris schon nahelegte. Zum Beenden eines Threads bietet sich die Verwendung eines Synchronisationsobjektes wie z.B. ein Event an. Das wird mit CreateEvent erzeugt, mit SetEvent gesetzt und kann mit WaitForSingleObject ausgewertet werden. Die Schleife in der Threadfunktion sollte zyklisch WaitForSingleObject aufrufen und den Rückgabewert auswerten, damit kann dem Thread sauber der Beendigungswunsch mitgeteilt werden. Eine Endlosschleife ohne Zeitrückgabe macht den Windows-Scheduler unglücklich. Welches Echtzeit-OS verwendest Du/Deine Firma denn da, etwa OnTime RTOS-32?
Danke für eure Hilfe, aber ich komme trotzdem nicht weiter. Kann mir einer ein Beispiel in Form eines Codes zeigen?
Wie kann ich überprüfen ob der Thread läuft? Ich habe mal einem Breakpoint in der ThreadFunktion gesetzt. Da hält mein Programm nicht an. Funktioniert nicht der Thread.
Ich habe im Netz ein kleines Beispiel entdeckt. Da funktioniert auch der Thread. Ich verstehe nicht warum dann mein Programm nicht funktioniert.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.