Visual C++: Gute Thread-Klasse!



  • Hallo.

    Ich arbeite das erste mal mit Visual C++ und möchte eine Thread-Klasse nutzen, so wie sie in Java üblich ist.

    Ich habe eine schöne Klasse bei http://kaisar-haque.blogspot.com/2007/07/c-nice-thread-class_23.html gefunden.

    Leider funktioniert mein Code nicht.

    Beschreibung: Ich starte den Thread und gebe dort "Ich bin ein Thread" aus. Dann gebe ich "Hello" aus. Ich bekomme aber nur die zweite Ausgabe. MyClass::run wird nicht ausgeführt, obwohl sie durch Thread::start virtuell aufgerufen werden sollte! Wer kann mir helfen?

    - Aktuelle Ausgabe -

    Hello
    

    - Erwünschte Ausgabe -

    Ich bin ein Thread!
    Hello
    

    - Alle meine Dateien -

    MyClass.cpp:

    #include "stdafx.h"
    #include "MyClass.h"
    
    void MyClass::run()
    {
    	//Add your threaded code here
    	printf("Ich bin ein Thead!\n");
    }
    
    int main()
    {
    	MyClass obj;
    	obj.start();
    
    	//the thread is running
    
    	printf("Hello!\n");
    
    	obj.stop();
    
    	//the thread is stopped
    	return 0;
    }
    

    MyClass.h:

    #include "stdafx.h"
    #include "Thread.h"
    
    class MyClass: public Thread
    {
    public:
    	void run();
    };
    

    Thread.cpp:

    #include "stdafx.h"
    #include "Thread.h"
    
    Thread::Thread()
    {
    	hThread = 0;
    }
    
    void Thread::start()
    {
    	_threadObj = this;
    
    	DWORD threadID;
    	hThread = CreateThread(0, 0, threadProc, _threadObj, 0, &threadID);
    }
    
    void Thread::stop()
    {
    	if (hThread)
    	{
    		TerminateThread (hThread, 0);
    	}
    }
    
    unsigned long __stdcall Thread::threadProc(void* ptr)
    {
    	((Thread*)ptr)->run();
    	return 0;
    }
    

    Thread.h:

    #include "stdafx.h"
    
    class Thread
    {
    
    public:
    
    	Thread();
    	void start();
    	void stop();
    	virtual void run() = 0;
    
    protected:
    
    	static unsigned long __stdcall threadProc(void* ptr);
    	Thread	*_threadObj;
    	HANDLE	hThread;
    
    };
    

    StdAfx.cpp:

    // stdafx.cpp : Quelltextdatei, die nur die Standard-Includes einbindet
    //	promoter.pch ist die vorkompilierte Header-Datei
    //	stdafx.obj enthält die vorkompilierte Typinformation
    
    #include "stdafx.h"
    
    // ZU ERLEDIGEN: Verweis auf alle zusätzlichen Header-Dateien, die Sie in STDAFX.H
    // und nicht in dieser Datei benötigen
    

    StdAfx.h:

    // stdafx.h : Include-Datei für Standard-System-Include-Dateien,
    //  oder projektspezifische Include-Dateien, die häufig benutzt, aber
    //      in unregelmäßigen Abständen geändert werden.
    //
    
    #if !defined(AFX_STDAFX_H__694B27B3_1B18_4787_90AD_6834DAB7B74F__INCLUDED_)
    #define AFX_STDAFX_H__694B27B3_1B18_4787_90AD_6834DAB7B74F__INCLUDED_
    
    #if _MSC_VER > 1000
    #pragma once
    #endif // _MSC_VER > 1000
    
    #define WIN32_LEAN_AND_MEAN		// Selten benutzte Teile der Windows-Header nicht einbinden
    
    #include <stdio.h>
    
    #include <windows.h>
    #include <iostream.h>
    #include <wincon.h>
    
    //{{AFX_INSERT_LOCATION}}
    // Microsoft Visual C++ fügt zusätzliche Deklarationen unmittelbar vor der vorherigen Zeile ein.
    
    #endif // !defined(AFX_STDAFX_H__694B27B3_1B18_4787_90AD_6834DAB7B74F__INCLUDED_)
    

    Wer kann mir helfen? Ich bin seit Stunden am Rätseln und komme nicht weiter!

    Ich habe alternativ auch http://www.linuxselfhelp.com/HOWTO/C++Programming-HOWTO-18.html probiert, jedoch bekomme ich nur massig Compilerfehler:

    Thread.cpp
    C:\Programme\Microsoft Visual Studio\MyProjects\promoter\Thread.cpp(16) : error C2146: Syntaxfehler : Fehlendes ';' vor Bezeichner 'ThreadId_'
    C:\Programme\Microsoft Visual Studio\MyProjects\promoter\Thread.cpp(16) : error C2501: 'ThreadID' : Fehlende Speicherklasse oder Typbezeichner
    C:\Programme\Microsoft Visual Studio\MyProjects\promoter\Thread.cpp(16) : error C2501: 'ThreadId_' : Fehlende Speicherklasse oder Typbezeichner
    C:\Programme\Microsoft Visual Studio\MyProjects\promoter\Thread.cpp(26) : error C2065: 'thread_create' : nichtdeklarierter Bezeichner
    C:\Programme\Microsoft Visual Studio\MyProjects\promoter\Thread.cpp(26) : error C2065: 'ThreadId_' : nichtdeklarierter Bezeichner
    C:\Programme\Microsoft Visual Studio\MyProjects\promoter\Thread.cpp(40) : error C2227: Der linke Teil von '->Run' muss auf Klasse/Struktur/Union zeigen
    C:\Programme\Microsoft Visual Studio\MyProjects\promoter\Thread.cpp(40) : error C2352: 'Thread::Arg' : Unzulaessiger Aufruf einer nichtstatischen Member-Funktion
            C:\Programme\Microsoft Visual Studio\MyProjects\promoter\Thread.cpp(13) : Siehe Deklaration von 'Arg'
    C:\Programme\Microsoft Visual Studio\MyProjects\promoter\Thread.cpp(44) : error C2723: 'Setup' : Speicherklassenbezeichner 'virtual' ungueltig fuer Funktionsdefinition
    C:\Programme\Microsoft Visual Studio\MyProjects\promoter\Thread.cpp(49) : error C2723: 'Execute' : Speicherklassenbezeichner 'virtual' ungueltig fuer Funktionsdefinition
    Fehler beim Ausführen von cl.exe.
    

    Ohne irgendwas verändert zu haben. In der Zeile "ThreadID ThreadId_;" (16) sehe ich ebenfalls keinen Semikolon-Vergessen-Syntaxfehler. Ich bin am Verzweifeln...

    Gruß
    Daniel



  • Ich vermute, dass dein Thread zwischen start und stop gar nicht dazu kommt, irgendetwas zu tun. Du killst ihn ja sofort wieder. Nebenbei gesagt, ist TerminateThread keine gute Idee.



  • Ich habe obj.stop(); auch schon auskommentiert. run() wird trotzdem nicht ausgeführt.

    Wie kann man das mit dem Terminate verbessern? Kennst du eine bessere Möglichkeit, OOP und Threads so elegant wie in Java zu kombinieren, sodass ich einfach nur noch von einer Thread-Klasse ableiten muss?



  • blackdrake schrieb:

    Wie kann man das mit dem Terminate verbessern?

    Ich würde warten, bis der Thread beendet ist. Eventuell ihm ein Zeichen geben, sodass er sich beendet.

    blackdrake schrieb:

    Kennst du eine bessere Möglichkeit, OOP und Threads so elegant wie in Java zu kombinieren, sodass ich einfach nur noch von einer Thread-Klasse ableiten muss?

    Es gibt noch Boost.Thread, womit ich mich aber nicht auskenne. Das wird aber womöglich in den nächsten C++-Standard aufgenommen. Ich habe aber bereits ein wenig mit den Threads von SFML gearbeitet, dort kannst du von einer Threadklasse erben und die Run() -Methode überschreiben.



  • Boost.Thread ist gut, und einfach zu verwenden.

    Die Implementierung ist ab 1.36 oder 1.37 auch recht gut (der originale Code von William Kempf war nicht so der Renner).



  • Hallo.

    Danke für eure Antworten.

    Bezüglich meines ersten Codes habe ich einen Erfolg erzielt. Lasse ich das Main-Programm warten (durch eine Endlosschleife), dann kommt auch die Thread-Ausgabe. Das wäre ja eigentlich schon gut, aber...

    Ich habe ein Problem mit diesen in VC++ eingebetteten CreateThread(): Ich finde einfach keine passende SetDetachState funktion, so wie es z.B. bei den POSIX-Threads möglich ist. Ich möchte meine Threads nämlich Detached haben, sodass sie ohne einen Bezug zum Ursprungsthread ablaufen und sich selbst beenden und automatisch den Speicher freigeben (Ich weiß, das macht man selten, aber ich habe einen sehr guten Grund dafür!) Die Verwendung des unsauberen .stop() habe ich folglich auch nicht vor.

    Wie sieht es mit Boost.Thread aus? Gibt es irgendwo ein Tutorial, wie ich diese Dinger in VC++ 6.0 einbinden und verwenden kann? Ich bin da absoluter Neuling. Und wie sieht es mit der Plattformunabhängigkeit aus? Ich möchte das Programm am Ende auf einem Linuxserver mit G++ Compiler laufen lassen. Das heißt, dort sollte Boost ebenfalls (stable) verfügbar und installiert sein. Gibt es dazu auch Anleitungen?

    Gruß
    Daniel



  • Ich habe ein Problem mit diesen in VC++ eingebetteten CreateThread(): Ich finde einfach keine passende SetDetachState funktion, so wie es z.B. bei den POSIX-Threads möglich ist. Ich möchte meine Threads nämlich Detached haben, sodass sie ohne einen Bezug zum Ursprungsthread ablaufen und sich selbst beenden und automatisch den Speicher freigeben (Ich weiß, das macht man selten, aber ich habe einen sehr guten Grund dafür!) Die Verwendung des unsauberen .stop() habe ich folglich auch nicht vor.

    Die Threads unter Windows sind anyway unabhängig voneinander.
    Für CreateThread(..) musst Du CloseHandle(..) aufrufen und ansonsten halt den Speicher freigeben, den Du allozierst.

    BTW: CreateThread(..) sollte nicht verwendet werden, falls Du Funktionen aus der C- Runtime verwendest (das ergibt Leaks).

    Verwende _beginthreadex(..) für Win32 Projekte oder AfxBeginThread(..) für MFC Projekte.

    ABER: wenn Du die Thread der Boost Library verwendest, musst Du dich nicht mit diesem APIs rumschlagen.

    Simon



  • Unter Linux ist es evtl. in den Repos, für VC++ 6.0 musst du dir schon boost runterladen, kompilieren mit boost.jam und so weiter, dazu gibt es Artikel hier im Forum.

    Zur Benutzung findest du hier etwas: http://www.boost.org/doc/libs/1_39_0/doc/html/thread.html

    und so sieht das ganze dann aus, imho viel eleganter als in Java:

    #include <boost/thread.hpp>
    
    using namespace boost;
    
    void foo()
      {
      //tue etwas
      }
    
    int main()
      {
      thread t(foo); //startet thread sofort
      //tue etwas anderes gleichzeitig
      }
    


  • Musst du wirklich Visual C++ 6 verwenden? Die Version ist hoffnungslos veraltet, hat einige Bugs und unterstützt den C++-Standard nur begrenzt.

    Sonst kannst du dir ja die kostenlose MSVC++ 2008 Express Version herunterladen...



  • Ich dachte mir, ich könnte für einen einfachen Zweck mal VC++ 6 rauskramen und ein wenig damit probieren. Aber gut, ich werde VC++ 2008 Express herunterladen und schauen, dass ich dort die Boost-Libraries installiere.

    Die Threads unter Windows sind anyway unabhängig voneinander.

    Bedeutet das, dass sie automatisch "detached" sind und ihren Thread-Handle automatisch freigeben, sobald der Thread zuendegelaufen ist? Was ist mit der Abhängigkeit zum aufrufenden Thread? Ein detached Thread sollte sich ja komplett von seinem Vater-Thread lösen (= das Ergebnis des Sohn-Threads gar nicht erwarten) - ist das auch erfüllt?



  • blackdrake schrieb:

    Bedeutet das, dass sie automatisch "detached" sind und ihren Thread-Handle automatisch freigeben, sobald der Thread zuendegelaufen ist?

    Schau mal hier: http://msdn.microsoft.com/de-de/library/kdzttdcb(VS.80).aspx

    You can call _endthread or _endthreadex explicitly to terminate a thread; however, _endthread or _endthreadex is called automatically when the thread returns from the routine passed as a parameter. Terminating a thread with a call to endthread or _endthreadex helps to ensure proper recovery of resources allocated for the thread.

    _endthread automatically closes the thread handle (whereas _endthreadex does not). Therefore, when using _beginthread and _endthread, do not explicitly close the thread handle by calling the Win32 CloseHandle API. This behavior differs from the Win32 ExitThread API.

    blackdrake schrieb:

    Was ist mit der Abhängigkeit zum aufrufenden Thread? Ein detached Thread sollte sich ja komplett von seinem Vater-Thread lösen (= das Ergebnis des Sohn-Threads gar nicht erwarten) - ist das auch erfüllt?

    Ja, die Threads sind voneinander unabhängig.


Anmelden zum Antworten