www.mikrocontroller.net

Forum: PC-Programmierung Thread in Klasse


Autor: X_Rider (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: X_Rider (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
class Thread
{
 private:

  HANDLE hThread;
  DWORD dwThreadID;  
  LPVOID pParam;

  enum Status
  {
    STOPPED,
    RUNNING,
    SUSPENDING
  };

  int status;
      
 public:
  
  void start ();
  void suspend ();
  void stop ();

  void setParameter(LPVOID pParam);

  virtual DWORD WINAPI my_Thread(void* unused);

  Thread();
  ~Thread();
};
Datei Thread.cpp
#include "thread.h"

void Thread::start()
{
  if (status == RUNNING) 
  {
    // Thread is running 
    return;
  }
  else if (status == SUSPENDING)
  {
    // Thread continue
    status = RUNNING;
     ResumeThread(hThread);
  }
  else
  {
    // Thread start
    status = RUNNING;
          hThread = CreateThread(NULL, Thread_STACK_SIZE, my_Thread,      0, 0, &dwThreadID);
  }
}

DWORD WINAPI Thread::my_Thread(void* unused)
{
  while (true)
  {        
     //Sleep(1000);
  }
  return (0);
}

Das verstehe ich nicht. Was mache ich falsch?

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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:
class Thread
{
 private:

  HANDLE hThread;
  DWORD dwThreadID;  
  LPVOID pParam;

  enum Status
  {
    STOPPED,
    RUNNING,
    SUSPENDING
  };

  int status;
      
 public:
  
  void start ();
  void suspend ();
  void stop ();

  void setParameter(LPVOID pParam);

 
  virtual DWORD ThreadFunction(void);


  Thread();
  ~Thread();
 
 protected:
  static DWORD WINAPI StaticThreadFunction(void* pthis);
};


und entsprechend :
#include "thread.h"

void Thread::start()
{
  if (status == RUNNING) 
  {
    // Thread is running 
    return;
  }
  else if (status == SUSPENDING)
  {
    // Thread continue
    status = RUNNING;
     ResumeThread(hThread);
  }
  else
  {
    // Thread start
    status = RUNNING;
          hThread = CreateThread(NULL, Thread_STACK_SIZE, StaticThreadFunction,      this, 0, &dwThreadID);
  }
}

DWORD WINAPI Thread::StaticThreadFunction(void* pthis)
{
  Thread *pThis;

  pThis = (Thread *) pthis;
  // jaaa, ich weiß. Das ist ein C-Typecast. 

  return pThis->ThreadFunction();

}


DWORD Thread::ThreadFunction(void)
{
  while (true)
  {        
     //Sleep(1000);
  }
  return (0);
}


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?

Autor: X_Rider (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.

Autor: Christoph __ (chris)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: X_Rider (Gast)
Datum:

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

Autor: X_Rider (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Christoph __ (chris)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: X_Rider (Gast)
Datum:

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

Autor: X_Rider (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: X_Rider (Gast)
Datum:
Angehängte Dateien:

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

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Poste doch mal DEIN Projekt, nicht ein anderes, das funktioniert.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.