Threads in C++ ?
-
Hallo again.
Ich suche schon längere Zeit eine einfache Möglichkeit einen Thread in C++ zu starten. Am liebsten wäre es mir in einer Java ähnliche Art, so dass eine C++ Klasse von einer Thread Klasse erbt.
Das :
http://msdn.microsoft.com/en-us/library/zfbxt3zs(VS.80).aspx
ließt sich ja schon mal ganz gut und ist in Python von Haus aus dabei, aber wie kann ich dies in C++ anwenden?Kann mir bitte da jemand helfen?
beste Grüße
grid
-
Möglichkeiten, welche dir zur Verfügung stehen:
- Warten bis C++0x
- http://www.boost.org/
Es hat sowieso allerhand nützlicher Bibliotheken und unteranderem eine Platform unabhängige Thread Bibliothek:
http://www.boost.org/doc/libs/1_35_0/doc/html/thread.html - Platform abhängig. Auf Windows entweder mit der MFC oder mit der WinAPI
Grüssli
-
Dravere schrieb:
Möglichkeiten, welche dir zur Verfügung stehen:
- Warten bis C++0x
- http://www.boost.org/
Es hat sowieso allerhand nützlicher Bibliotheken und unteranderem eine Platform unabhängige Thread Bibliothek:
http://www.boost.org/doc/libs/1_35_0/doc/html/thread.html - Platform abhängig. Auf Windows entweder mit der MFC oder mit der WinAPI
Grüssli
Danke hmmm...das ist ja leider alles nicht so das gelbe vom Ei^^
Da würde ich ja noch die Windows Variante vorziehen, hat da vielleicht jemand ein Codebeispiel?
Oder...ich möchte später zur GUI-Erstellung Qt benutzen und hab gelesen das Qt auch Multithreading unterstützt und von Java Swing kenne ichs auch das ich aus Swing heraus etwas in einem Thread starten lässt. Geht das mit Qt auch?
beste Grüße
grid
-
Qt hat eigene Threading-Klassen. QThread wars, wenn ich mich richtig entsinnen kann. Das ist sogar java-like aufgezogen. Man erbt von QThread oder QRunnable und implementiert die run()-Methode aus.
-
Ja, das geht natürlich auch. Qt hat eine Thread-Klasse soweit mir das bekannt ist. Gibt natürlich noch viele andere Threadklassen im Web. Auch wxWidgets bietet Threads an.
Aus persönlicher Erfahrung kann ich allerdings nur die Thread Bibliothek von boost empfehlen, da sie in wirklich gutem C++ geschrieben ist und auch Typsicherheit aufweist, bzw. allgemein sehr flexibel ist. Man leitet zwar nicht ab, aber das ist gar nicht nötig, da die Klasse viel mächtiger ist.Für Codebeispiel in der WinAPI, schaust du am besten meinen Link an und klickst dich ein wenig durch. Zu den einzelnen Funktionen gibt es eigentlich immer noch Code-Beispiele.
Grüssli
-
Dravere schrieb:
Ja, das geht natürlich auch. Qt hat eine Thread-Klasse soweit mir das bekannt ist. Gibt natürlich noch viele andere Threadklassen im Web. Auch wxWidgets bietet Threads an.
Aus persönlicher Erfahrung kann ich allerdings nur die Thread Bibliothek von boost empfehlen, da sie in wirklich gutem C++ geschrieben ist und auch Typsicherheit aufweist, bzw. allgemein sehr flexibel ist. Man leitet zwar nicht ab, aber das ist gar nicht nötig, da die Klasse viel mächtiger ist.Für Codebeispiel in der WinAPI, schaust du am besten meinen Link an und klickst dich ein wenig durch. Zu den einzelnen Funktionen gibt es eigentlich immer noch Code-Beispiele.
Grüssli
Also ich finde dan Qt-Ansatz (obwohl mir Qt an sich eigentlich nicht sehr gut gefällt) was das Threading betrifft sehr gut. Was ist daran nicht typsicher? Oder unflexibel? Was ist mächtiger am Boost-Ansatz?
-
Ja super, nun bin ich beruhigt^^
Wenn man wie ich jahrelang mit Java und Python "verwöhnt" wurde und nun plötzlich in die C++ Pointer und Headerfiles Welt geworfen wird muss man sich schon etwas umstellen^^
beste Grüße
grid
-
Für Windows hätte ich noch das hier anzubieten:
Exceptions:
struct ExcSyscallError : public std::runtime_error { ExcSyscallError( ) : runtime_error("system call error") { } }; struct ExcThreadingError : public std::runtime_error { ExcThreadingError( const std::string& cErr_ ) : runtime_error("threading error: " + cErr_) { } };Threads.h
class CRunnable { public: virtual void* run () = 0; virtual ~CRunnable ( ) = 0; }; //class CRunnable /* =========================================================================================== */ /* =========================================================================================== */ class CThread { public: enum EPriority { LOWEST = THREAD_PRIORITY_LOWEST, LOWER = THREAD_PRIORITY_BELOW_NORMAL, NORMAL = THREAD_PRIORITY_NORMAL, HIGHER = THREAD_PRIORITY_ABOVE_NORMAL, HIGH = THREAD_PRIORITY_HIGHEST, REAL_TIME = THREAD_PRIORITY_TIME_CRITICAL }; CThread ( std::auto_ptr<CRunnable> p_poRunnable , EPriority p_ePriority = NORMAL ); CThread ( EPriority p_ePriority = NORMAL ); virtual ~CThread ( ); void start ( ); void* join ( unsigned int p_nTimeout = INFINITE ); bool isAlive ( ); private: CThread (const CThread& p_oThread); const CThread& operator= (const CThread& p_oThread); virtual void* run ( ) { return NULL ; } static DWORD WINAPI startThreadRunable ( PVOID p_pParam ); static DWORD WINAPI startThread ( PVOID p_pParam ); HANDLE m_hThread; std::auto_ptr<CRunnable> m_poRunnable; bool m_bIsAlive; void* m_pResult; }; //class CThreadThreads.cpp
CRunnable::~CRunnable ( ) { } CThread::CThread ( std::auto_ptr<CRunnable> p_poRunnable , EPriority p_ePriority ) : m_hThread(NULL) , m_poRunnable(p_poRunnable) , m_bIsAlive(false) , m_pResult(NULL) { if( m_poRunnable.get() == NULL ) { throw ExcThreadingError("invalid runnable object"); } m_hThread = CreateThread ( NULL , 0x00 , startThreadRunable , this , CREATE_SUSPENDED , NULL ); if(!m_hThread) { throw ExcSyscallError(); } if( p_ePriority != NORMAL ) { SetThreadPriority ( m_hThread , p_ePriority ); } } CThread::CThread ( EPriority p_ePriority ) : m_hThread(NULL) , m_poRunnable(NULL) , m_bIsAlive(false) , m_pResult(NULL) { m_hThread = CreateThread ( NULL , 0x00 , startThread , this , CREATE_SUSPENDED , NULL ); if(!m_hThread) { throw ExcSyscallError(); } if( p_ePriority != NORMAL ) { SetThreadPriority ( m_hThread , p_ePriority ); } } CThread::~CThread ( ) { if(!m_bIsAlive) { ResumeThread ( m_hThread ); } WaitForSingleObject ( m_hThread , INFINITE ); CloseHandle ( m_hThread ); } void CThread::start ( ) { if(!m_hThread) { throw ExcThreadingError("Prior thread creation failed."); } m_bIsAlive = true; if(ResumeThread( m_hThread ) == 0xFFFFFFFF ) { m_bIsAlive = false; throw ExcSyscallError(); } } void* CThread::join ( unsigned int p_nTimeout ) { WaitForSingleObject ( m_hThread , p_nTimeout ); return m_pResult; } bool CThread::isAlive ( ) { return m_bIsAlive; } DWORD WINAPI CThread::startThreadRunable ( PVOID p_pParam ) { CThread* pThread = reinterpret_cast<CThread*>(p_pParam); pThread->m_bIsAlive = true; pThread->m_pResult = pThread->m_poRunnable->run ( ); pThread->m_bIsAlive = false; return reinterpret_cast<unsigned int>(pThread->m_pResult); } DWORD WINAPI CThread::startThread ( PVOID p_pParam ) { CThread* pThread = reinterpret_cast<CThread*>(p_pParam); if(pThread->m_bIsAlive) { pThread->m_pResult = pThread->run ( ); } pThread->m_bIsAlive = false; return reinterpret_cast<unsigned int>(pThread->m_pResult); }Leider nicht kommentiert.
Entweder man erbt direkt von CThread oder von CRunnable.
Wenn man von CRunnable erbt, muss man ein CThread-Objekt erzeugen, und ein Objekt das run aus CRunnable implementiert übergeben.
-
Tachyon schrieb:
Also ich finde dan Qt-Ansatz (obwohl mir Qt an sich eigentlich nicht sehr gut gefällt) was das Threading betrifft sehr gut. Was ist daran nicht typsicher? Oder unflexibel? Was ist mächtiger am Boost-Ansatz?
Ich kenne den QT-Ansatz nicht. QT hat mich schon immer nur wegen dem PrePreprocessor abgeschreckt und ich habe mir die Bibliothek nie richtig angeschaut. Zudem viel zu teuer, falls man damit proprietäre Software erstellen möchte.
Kennen tue ich nur den MFC, WinAPI, Java und wxWidgets Ansatz. Zumindest was den Bereich C++ oder ähnlich C++ betrifft.
Boost.Thread ist einfach schon nur zum arbeiten praktisch, da es sehr leicht und einfach gehalten ist. Mutex und Lock Klassen sind sehr angenehm und leicht verständlich aufgebaut. Condition Variables ist auch ein absolutes plus.Was einem richtig gefallen kann an Boost.Thread ist der Einsatz von Templates. Zum Beispiel nur um mal den Konstruktur anzuschauen:
template<typename Callable> thread(Callable callable);Anders gesagt, die typische Run-Funktion kann alles sein, was man aufrufen kann. Und wenn man sowas nun mit Boost.Bind oder den zukünftigen Lambda Ausdrück verbindet, dann eröffnet dies einem ungeahnte Möglichkeiten.
Du kannst über die gleiche Klasse ThreadKlassen laufen lassen, wie auch normale statische Klassen oder gar Memberfunktionen. Es ist einfach alles möglich.Und du musst dich nicht mal um den Speicher des thread Objektes kümmern. Ein thread objekt kannst du auf dem Stack erstellen und einfach wieder kaputt gehen lassen, der Thread läuft weiter. Ausser du willst natürlich etwas mit dem Objekt machen, dann musst es halt irgendwo ablegen

Um auf den aktuellen Thread zugreifen zu können, gibt es einfach einen eigenen namespace this_thread, welcher einem alle nötigen Funktionen liefert.
Am besten liest du mal die Bibliothek durch

Grüssli
-
Dravere schrieb:
Und du musst dich nicht mal um den Speicher des thread Objektes kümmern. Ein thread objekt kannst du auf dem Stack erstellen und einfach wieder kaputt gehen lassen, der Thread läuft weiter. Ausser du willst natürlich etwas mit dem Objekt machen, dann musst es halt irgendwo ablegen

Das ein Thread ohne Objekt existiert finde ich merkwürdig...
Ich kenne Boost schon, und benutze es auch selbst gerne. Nur sehe ich, was die Flexibilität betrifft keine Vorteile gegenüber der Vererbung. Die run()-Methode kann ja auf alle lokalen Variablen des Objekts zugreifen, das von Thread/Runnable erbt. Dadurch ist es nicht notwendig, Funktionen mit beliebigem Interface als Threadfunktion auslegen zu können.
-
also ich persönlich ziehe die winapi vor
#include <windows.h> DWORD WINAPI startFunktion(LPVOID Data) { // blablabla Berechnungen return DWORD(Data); } int main() { HANDLE h; DWORD d; h = CreateThread(NULL, 0, startFunktion, 0, 0, &d); return 0; }nach dem schema klappt das dann
-
Die Klasse, die ich oben reingestellt habe, gapselt die WinAPI mehr oder weniger ordentlich ein.
-
Tachyon schrieb:
Dravere schrieb:
Und du musst dich nicht mal um den Speicher des thread Objektes kümmern. Ein thread objekt kannst du auf dem Stack erstellen und einfach wieder kaputt gehen lassen, der Thread läuft weiter. Ausser du willst natürlich etwas mit dem Objekt machen, dann musst es halt irgendwo ablegen

Das ein Thread ohne Objekt existiert finde ich merkwürdig...
Ich kenne Boost schon, und benutze es auch selbst gerne. Nur sehe ich, was die Flexibilität betrifft keine Vorteile gegenüber der Vererbung. Die run()-Methode kann ja auf alle lokalen Variablen des Objekts zugreifen, das von Thread/Runnable erbt. Dadurch ist es nicht notwendig, Funktionen mit beliebigem Interface als Threadfunktion auslegen zu können.
Es hat NUR Vorteile. Wenn du diese nicht sehen kannst... kann ich auch nix machen.
Ich finde es auf jeden Fall vorteilhaft wenn ich nicht eine Klasse von irgendwas ableiten muss damit ich irgendeine blöde Funktion überschreiben kann, sondern wenn ich einfach meinen Funktor übergeben kann.
Oder wenn ich eine bereits bestehende Klasse verwenden kann, die ganz zufälligerweise nicht von der entsprechenden Thread Klasse erbt.
-
Hast Du auch irgendwelche konkreten Argumente, oder geht es hier nur um Deinen persönlichen Geschmack?
-
Tachyon schrieb:
Hast Du auch irgendwelche konkreten Argumente, oder geht es hier nur um Deinen persönlichen Geschmack?
Ich würde sagen wenn ist es nicht nur der persönliche Geschmack von hustbaer. Seine Argumente hat er imho auch gebracht. Ein Aufzwingen von Vererbung halte ich für schlechter als die Boostvariante (wo ich immer noch die Option habe eine beliebige Methode oder Funktion über den Funktor zu kapseln).
Ich finde die Boostvariante auf jedenfall flexibler.
cu André
-
Da würde ich ja noch die Windows Variante vorziehen, hat da vielleicht jemand ein Codebeispiel?
http://www.henkessoft.de/C++/MFC/mfc_einsteigerbuch_kapitel17.htm
-
asc schrieb:
Tachyon schrieb:
Hast Du auch irgendwelche konkreten Argumente, oder geht es hier nur um Deinen persönlichen Geschmack?
Ich würde sagen wenn ist es nicht nur der persönliche Geschmack von hustbaer. Seine Argumente hat er imho auch gebracht. Ein Aufzwingen von Vererbung halte ich für schlechter als die Boostvariante (wo ich immer noch die Option habe eine beliebige Methode oder Funktion über den Funktor zu kapseln).
Ich finde die Boostvariante auf jedenfall flexibler.
cu André
Also ich sehe da keine Agrumente. ...halte ich für besser... und ...ich finde... sind für mich keine Argumente.
Ein Argument ist für mich: XY ist besser, weil...
Von der Flexibilität tun sich beide Varianten nichts.
-
Tachyon schrieb:
Von der Flexibilität tun sich beide Varianten nichts.
Doch, gerade dort nehmen sie sich viel. Bei deiner Variante bin ich gezwungen abzuleiten - was je nach Ursprung der aufgerufenen Funktion/Methode gegebenenfalls garnicht möglich ist, ohne eine Proxyklasse zu schreiben. Und bestehende Klassen muss ich umschreiben um sie damit zum laufen zu bekommen.
cu André
-
Hast Du mal ein Beispiel, wo diese Möglichkeit wirklich sinnvoll einsetzbar ist, ohne dass die Nachteile zum Tragen kommen, weil z.B. die zum Thread gemachte Funktion nicht dazu taugt, als Thread ausgelagert zu werden?
Also ein nicht hypothetisches Beispiel mit Hand und Fuss, wo einem der Code nicht um die Ohren fliegt?
-
Ein Codebeispiel ist eigentlich ziemlich irrelevant. Sehr wahrscheinlich, kann man mit QTThread, bzw. mit jeglichem Thread-Framework, die jeweiligen Möglichkeiten des anderen nachbauen. Es fragt sich halt nur wie?
Boost.Thread ist da halt sehr elegant und erlaubt es einem unkompliziert und mit wenig Codezeilen alles nachzubauen. Wenn du uns das nicht glaubst, dann musst du das nicht, aber es wäre zu empfehlen, dass du es vielleicht mal ausprobierst.Ich meine ich kann dir schon ein x-beliebiges Beispiel hinklatschen:
void a_function(); // <- Die ist z.B. aus irgendeinem Grund fix. Soll jetzt aber in einem Thread laufen int main { boost::thread t(&a_function); // <- Das ist alles! return 0; }Aber man könnte es durchaus auch so halten:
void 1st_function(int); // wieder fix. void 2nd_function(float, int); // auch fix. // Und noch eine fixe Klasse, zum Beispiel aus einer fremden Bibliothek! class CClass { // Whatever public: void expensive_calculation(); } int main() { boost::thread t1(boost::bind(&1st_function, 234)); // Thread 1 boost::thread t2(boost::bind(&1st_function, 500)); // Thread 2 boost::thread t3(boost::bind(&2nd_function, 0.1f, 450)); // Thread 3 CClass MyClass; MyClass.expensive_calculation(); // Das geht mir zu lange, also anders. boost:thread t4(boost::bind(&CClass::expensive_calculation, &MyClass)); // Thread 4 t1.join(); // oder auch nicht t2.join(); // oder auch nicht t3.join(); // oder auch nicht t4.join(); // Naja, hier müssen wir, da MyClass nicht zerstört werden darf. return 0; }Die joins hätte man auch sehr schön über thread_group lösen können, aber das ist wieder was anderes. Schau dir dazu die Library an.
Und Synchronizationen sind auch ganz einfach:
boost::mutex g_Mutex; typedef boost::lock_guard<boost::mutex> t_Sync; void anywhere() { { t_Sync Sync(g_Mutex); // Synchronisiert. } }Dann kannst du dir gerne noch die conditional variables oder barrier anschauen. Es ist kurz, einfach und schnell gehalten und deswegen auch extrem flexibel.
Denk dir irgendeinen Code mit QTThread aus. Ich bin mir sicher, dass man es gleich oder besser lösen kann.
Grüssli