Forum: PC-Programmierung Thread in Klasse


von X_Rider (Gast)


Lesenswert?

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?

von X_Rider (Gast)


Lesenswert?

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?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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?

von X_Rider (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Christoph _. (chris)


Lesenswert?

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.

von X_Rider (Gast)


Lesenswert?

Ich verwende Microsoft Visual Studio C++.NET 2003.
Kannst du mir einen Beispielcode geben?

von X_Rider (Gast)


Lesenswert?

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!

von Christoph _. (chris)


Lesenswert?

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

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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?

von X_Rider (Gast)


Lesenswert?

Danke für eure Hilfe, aber ich komme trotzdem nicht weiter.
Kann mir einer ein Beispiel in Form eines Codes zeigen?

von X_Rider (Gast)


Lesenswert?

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.

von X_Rider (Gast)


Angehängte Dateien:

Lesenswert?

Ich habe im Netz ein kleines Beispiel entdeckt.
Da funktioniert auch der Thread. Ich verstehe nicht warum dann mein 
Programm nicht funktioniert.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Poste doch mal DEIN Projekt, nicht ein anderes, das 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
Noch kein Account? Hier anmelden.