[win32 & linux] threads c++

Description

Il s'agit d'un classe qui encapsule l'execution d'un thread Win32/POSIX.

Cette classe offre à l'utilisateur une interface de haut niveau pour contrôler l'execution du thread (démarrer, arreter, attendre, ...).

Un programme d'exemple est fourni avec le source thread.h/.cpp pour guider l'utilisateur dans l'utilisation de cette classe.

Ce source compile sous Win32 et sous GNU/Linux (ne pas oublier de linker avec pthread.lib sous Linux).

Il y a deux moyens d'exploiter cette classe :

1) créer une classe qui dérive de "Filament" et qui surcharge la fonction membre "void Filament::Traitement()". L'exécution du thread est controlée par Filament::Demarrer(), ::Arreter(), Attendre().

2) créer une classe quelconque ("foo" pour l'exemple) contenant une fonction membre quelconque ("void foo:operator()()" pour l'exemple). Instancier alors la classe quelconque ("foo_inst" pour l'exemple) puis la classe "Filament_Avance" avec pour argument (dans le constructeur) les adresses de la classe et de la fonction membre : Filament_Avance<foo> monfil(&foo_inst, void &foo::operator()). Le thread démarre immédiate et execute le contenu de foo:operator().

Bonne chance...
Xter.

Source / Exemple :


// Librairies C++
#include<string>
#include<iostream>

// Librairies supplémentaires 
#ifdef WIN32
	#include<process.h>
#else
	#include<unistd.h>
    #include<pthread.h>
#endif

// Classe de base filament
class x_Filament 
{
public:
		
	// Constructeur
	x_Filament();
		
	// Constructeur par recopie
	x_Filament( const x_Filament & );
		
	/// Destructeur
	virtual ~x_Filament();
		
	// Démarre le filament.
    void Demarrer();
		
	// Traitement exécuté par le filament.
	virtual void Traitement() = 0;
		
	// Arrète le filament.
    void Arreter();
		
	// Attendre la fin du filament.
    void Attendre();
		
	// Etat du filament
	bool bExecution;
		
private:
		
	// Filament
	#ifdef WIN32
	static unsigned __stdcall Filament( void * pthis );
	#else
	static void * Filament( void * pthis );
	#endif
		
	// Identifiants
	#ifdef WIN32
	HANDLE Identifiant_Filament;
	unsigned int Identifiant_Filament_Win32;
	#else
	pthread_t Identifiant_Filament;
	#endif
};

// Classe derivée filament "amelioré"
template< class T_CLASSE >
class x_Filament_Ameliore : public x_Filament
{
public:
		
	// Constructeurs : l'un pour les fonctions membres normales, l'autre pour les fonctions membres 'const'	
	x_Filament_Ameliore( T_CLASSE * c, void (T_CLASSE::*m)() );
	x_Filament_Ameliore( T_CLASSE * c, void (T_CLASSE::*m)() const );
	
	// Traitement exécuté par le filament : execute l'instruction (classe->*methode)()
	void Traitement();
		
private:
		
	// Adresses de la classe
	T_CLASSE * classe;
		
	// Adresse de la méthode
	void (T_CLASSE::*methode_normale) ();
	void (T_CLASSE::*methode_const) () const;
		
	// Type de méthode
	const bool bConst;
};

template < class _CLASSE >
x_Filament_Ameliore< _CLASSE >::x_Filament_Ameliore( _CLASSE * c, void(_CLASSE::*m)() ) :
bConst(false)
{
	classe = c;
	methode_normale = m;
	Demarrer();
}

template < class _CLASSE >
x_Filament_Ameliore< _CLASSE >::x_Filament_Ameliore( _CLASSE * c, void(_CLASSE::*m)() const ) :
bConst(true)
{
	classe = c;
	methode_const = m;
	Demarrer();
}

template < class _CLASSE >
void x_Filament_Ameliore<_CLASSE >::Traitement() {
	if( bConst )
		(classe->*methode_const)();
	else
		(classe->*methode_normale)();
}

#ifdef WIN32
unsigned __stdcall x_Filament::Filament( void * pthis ) {
#else
void * x_Filament::Filament( void * pthis ) {
#endif
	try
	{			
		x_Filament * pParent = (x_Filament*)pthis;
		pParent->bExecution = true;
		pParent->Traitement();
	}
	catch(...)
	{
	}
	#ifdef WIN32
	return(0);
	//_endthreadex(0);
	#else
	pthread_exit(0);
	#endif
}

x_Filament::x_Filament() : bExecution( false ) { }

x_Filament::~x_Filament() {
	if( bExecution )
	{
		#ifdef WIN32
		int resultat;
		resultat = CloseHandle( Identifiant_Filament );
		if( resultat == 0 )
			std::cout << std::endl << "Erreur avec CloseHandle()! Error = "  << GetLastError() << "." << std::endl;
		#else
		pthread_detach( Identifiant_Filament );
		#endif
	}
}

void x_Filament::Demarrer() {
	if( !bExecution ) 
	{
		#ifdef WIN32
		Identifiant_Filament = (HANDLE)_beginthreadex( NULL, 0, &Filament, this, 0, &Identifiant_Filament_Win32 );
		if( Identifiant_Filament == 0 )
			std::cout << std::endl << "Erreur avec _beginthread()!"  << std::endl;
		#else
		int valeur = pthread_create( &Identifiant_Filament, 0, Filament, this );
		if( valeur != 0 )
			std::cout << std::endl << "Erreur avec pthread_create()!"  << std::endl;
		#endif
	}
}

void x_Filament::Arreter() {
	if( bExecution ) {
		#ifdef WIN32
		TerminateThread( Identifiant_Filament, 0 );		
		#else
		pthread_cancel( Identifiant_Filament );
		#endif
		bExecution = false;
	}
}

void x_Filament::Attendre() {
	if( bExecution ) {
		#ifdef WIN32
		int resultat;
		resultat = WaitForSingleObject( Identifiant_Filament, INFINITE );
		if( resultat != WAIT_OBJECT_0 )
			std::cout << std::endl << "Erreur avec WaitForSingleObject()! Error = " << GetLastError() << "." << std::endl;
		resultat = CloseHandle( Identifiant_Filament );
		if( resultat == 0 )
			std::cout << std::endl << "Erreur avec CloseHandle()! Error = "  << GetLastError() << "." << std::endl;
		#else
		if( pthread_join( Identifiant_Filament, NULL ) != 0 )
			std::cout << std::endl << "Erreur avec pthread_join()!"  << std::endl;
		#endif
		bExecution = false;
	}		
}

Conclusion :


Bug connu : Ne pas appeler filament::attendre() juste apres filament::demarrer(), faire une pause sinon le thread à même pas le temps de se lancer et donc l'identifiant du thread n'est pas positioné.

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.