thread et passage de parametre

Le
Bruno Causse
bonjour,

pour les threads je ne sert d'une strategie a la "java"

une classe abstraite pure (Interface)

class Runnable{
public:
virtual void run() = 0;
}

un classe thread tres simple (un simple wrapper des threads POSIX)

class POSIXThread {
pthread_t _threadID;

public :

POSIXThread(Runnable *ptr);
virtual ~POSIXThread() {};

void start();

protected:

static void* threadfunction(void * pt);
Runnable* _threadObj;
};

class RXEngine : public Runnable {

/

//variable d'instance
RXParamThread param;

//metode d'instance
void dansUneFonctionInit();
virtual void run();

};

void RXEngine::dansUneFonctionInit() {

param.init();
RXThread thread(this);
thread.start();

}

void RXEngine::run() {

//ici je peu utiliser la variable d'instance param

}

il y a t'il une facon plus elegante des passer la variable "param"

merci
--
Bruno Causse
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses Page 1 / 3
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Michael DOUBEZ
Le #16783821
Bruno Causse a écrit :
bonjour,

pour les threads je ne sert d'une strategie a la "java"

une classe abstraite pure (Interface)

class Runnable{
public:
virtual void run() = 0;
}


[snip]

class RXEngine : public Runnable {



[snip]
void RXEngine::dansUneFonctionInit() {

param.init();
RXThread thread(this);
thread.start();
}

void RXEngine::run() {

//ici je peu utiliser la variable d'instance param

}

il y a t'il une facon plus elegante des passer la variable "param"



Tu peux utiliser des classes proxy afin d'éviter de polluer l'interface
de RXEngine. En particulier si tu veux pouvoir lancer différentes
méthodes de l'objet.

Si c'est pour lancer des méthodes protégées, tu peux le faire avec des
classe imbriquées ou les déclarer friend et mettre l'implémentation
dans le fichier de code (pour moins polluer).

//exemple simple avec classe imbriquée:
class RXEngine{

struct RunFoo: public Runnable
{
int param1;
string param2;
RXEngine* owner;

void run(){ owner->foo(param1,param2); }
};
//....
};

void RXEngine::dansUneFonctionInit()
{
RunFoo* runit=new RunFoo;
runit->param1B;
param1->param2"secret";
param1->owner=this;

RXThread thread(runit);
thread.start();
}


Après c'est le début des problèmes :)

--
Michael
Bruno Causse
Le #16783941
"Michael DOUBEZ" 48c8f556$0$7602$

Après c'est le début des problèmes :)



tu peux developper un peu :)

merci
Michael DOUBEZ
Le #16784381
Bruno Causse a écrit :
"Michael DOUBEZ" 48c8f556$0$7602$
Après c'est le début des problèmes :)



tu peux developper un peu :)



Principalement, la gestion de la synchronisation entre l'arrêt du thread
et la destruction de l'objet, et le cas d'un thread qui meure
brutalement et donc ne dépile pas sa stack (au revoir RAII).

Il y a aussi certaines précautions à prendre comme mettre un catch à la
base de ta fonction dans le thread (et aussi comment remonter l'erreur à
l'objet appelant).

--
Michael
James Kanze
Le #16784741
On Sep 11, 11:21 am, "Bruno Causse"
pour les threads je ne sert d'une strategie a la "java"



une classe abstraite pure (Interface)



class Runnable{
public:
virtual void run() = 0;
}



un classe thread tres simple (un simple wrapper des threads POSIX)



class POSIXThread {
pthread_t _threadID;



public :



POSIXThread(Runnable *ptr);
virtual ~POSIXThread() {};



void start();



protected:



static void* threadfunction(void * pt);



Si ça fait ce que je pense (c-à-d être passer à pthread_create),
le code ne doit pas compiler. (Il y a un bug à cet égard dans
g++, mais d'autres compilateurs C++ s'en plaignent, comme il
faut.)

Runnable* _threadObj;
};



class RXEngine : public Runnable {



.../...



//variable d'instance
RXParamThread param;



//metode d'instance
void dansUneFonctionInit();
virtual void run();



};



void RXEngine::dansUneFonctionInit() {



param.init();
RXThread thread(this);
thread.start();



}



void RXEngine::run() {



//ici je peu utiliser la variable d'instance param



}



il y a t'il une facon plus elegante des passer la variable
"param"



Plus élégante que quoi ? Tu crées une classe dérivée de
Runnable, avec les membres qu'il te faut. Tu le constructes, ce
qui initialise les membres (éventuellement avec les paramètres
passés au constructeur), et tu passes l'objet à un POSIXThread,
qui démarre le thread. Le thread a bien accès à tous les membres
de la classe, y compris ceux initialisés à partir des paramètres
du constructeur. Quelque chose du genre :

RXEngine engin( ... ) ;
POSIXThread thread( &engin ) ;
thread.start() ;

La seule difficulté réele, c'est la gestion de la durée de vie
des objets.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Bruno Causse
Le #16785311
"James Kanze"

static void* threadfunction(void * pt);





Si ça fait ce que je pense (c-à-d être passer à pthread_create),
le code ne doit pas compiler. (Il y a un bug à cet égard dans
g++, mais d'autres compilateurs C++ s'en plaignent, comme il
faut.)




void* POSIXThread::threadFunction(void* pt) {

Runnable* runnable = static_cast< Runnable*>(pt);


runnable->run();


return NULL;

}

oui j'utilise g++ (seul gratuit sur macos),pas un warning.

Plus élégante que quoi ? Tu crées une classe dérivée de
Runnable, avec les membres qu'il te faut. Tu le constructes, ce
qui initialise les membres (éventuellement avec les paramètres
passés au constructeur), et tu passes l'objet à un POSIXThread,
qui démarre le thread. Le thread a bien accès à tous les membres
de la classe, y compris ceux initialisés à partir des paramètres
du constructeur. Quelque chose du genre :



> RXEngine engin( ... ) ;
> POSIXThread thread( &engin ) ;
> thread.start()

c'est exactement ma solution ? non?

La seule difficulté réele, c'est la gestion de la durée de vie
des objets.



oui, mes threads ont la durée de vie du programme.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
James Kanze
Le #16786131
On Sep 11, 3:36 pm, "Bruno Causse"
"James Kanze"



>> static void* threadfunction(void * pt);
>Si ça fait ce que je pense (c-à-d être passer à pthread_create),
>le code ne doit pas compiler. (Il y a un bug à cet égard dans
>g++, mais d'autres compilateurs C++ s'en plaignent, comme il
>faut.)



void* POSIXThread::threadFunction(void* pt) {



Runnable* runnable = static_cast< Runnable*>(pt);



runnable->run();



return NULL;
}



oui j'utilise g++ (seul gratuit sur macos),pas un warning.



C'est moins ce que fait la fonction qu'où on s'en sert qui
m'intéressait. L'appel :

pthread_create( ..., &threadFunction, ... ) ;

doit provoquer une erreur à la compilation, parce que le type
n'est pas bon. Mais comme j'ai dit, g++ boggue ici.

Le problème, c'est que le linkage fait partie du type. On
pourrait très bien imaginer un système où le C et le C++
utilisaient des conventions différentes de l'appel, et puisque
pthread_create va appeler la fonction comme si c'était une
fonction C, ça ne pourrait pas marcher. Du coup, si une fonction
s'attend à un pointeur à une fonction avec linkage C, on ne peut
pas lui en passer l'adresse d'une avec linkage C++.

>Plus élégante que quoi ? Tu crées une classe dérivée de
>Runnable, avec les membres qu'il te faut. Tu le constructes, ce
>qui initialise les membres (éventuellement avec les paramètres
>passés au constructeur), et tu passes l'objet à un POSIXThread,
>qui démarre le thread. Le thread a bien accès à tous les membres
>de la classe, y compris ceux initialisés à partir des paramètres
>du constructeur. Quelque chose du genre :



> RXEngine engin( ... ) ;
> POSIXThread thread( &engin ) ;
> thread.start()



c'est exactement ma solution ? non?



C'est plus ou moins ce qu'il me semblait, mais je ne voyais pas
ce que venait faire cette fonction d'initialisation là-dedans.

>La seule difficulté réele, c'est la gestion de la durée de
>vie des objets.



oui, mes threads ont la durée de vie du programme.



Dans quel cas, pas de problème:-). Tu les alloues dynamiquement,
et tu n'en fais jamais de delete, et ça marche.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Sylvain SF
Le #16789531
Michael DOUBEZ wrote on 11/09/2008 13:58:

Après c'est le début des problèmes :)



tu peux developper un peu :)



Principalement, la gestion de la synchronisation entre l'arrêt du thread
et la destruction de l'objet, et le cas d'un thread qui meure
brutalement et donc ne dépile pas sa stack (au revoir RAII).



l' "object" ? celui qui représente ce thread justement ?
après le passage par une fonction système qui invoque une fonction
static de cet object, on récupère cette instance pour appeler son 'run'
l'arrêt du thread doit correspondre à la vie de fin de cet objet (et
vice versa d'ailleurs).

la mort brutale doit être gérée, comme tu le dis, via un catch à plus
haut niveau (run itself sûrement).

Il y a aussi certaines précautions à prendre comme mettre un catch à la
base de ta fonction dans le thread (et aussi comment remonter l'erreur à
l'objet appelant).



présumer de l'existence de cet appelant est risqué / douteux.
je broadcasterais plutôt un message.

Sylvain.
Sylvain SF
Le #16789521
James Kanze wrote on 11/09/2008 16:50:

oui, mes threads ont la durée de vie du programme.



Dans quel cas, pas de problème:-). Tu les alloues dynamiquement,
et tu n'en fais jamais de delete, et ça marche.



pourquoi pas:

void* POSIXThread::threadFunction(void* ref){
Runnable* runnable = static_cast<Runnable*>(ref);
if (runnable){
try {
runnable->run();
}
catch (...){
}
delete runnable
}
return NULL;
}

??

purger proprement des threads à durée de vie courte ne me
parait pas tordu ?!

Sylvain.
James Kanze
Le #16790901
On Sep 11, 11:43 pm, Sylvain SF
Michael DOUBEZ wrote on 11/09/2008 13:58:
>>> Après c'est le début des problèmes :)



>> tu peux developper un peu :)



> Principalement, la gestion de la synchronisation entre
> l'arrêt du thread et la destruction de l'objet, et le cas
> d'un thread qui meure brutalement et donc ne dépile pas sa
> stack (au revoir RAII).



l' "object" ? celui qui représente ce thread justement ?



Dans la solution proposée, il y a en fait deux objets,
l'abstraction du thread même, et le « runnable » qui contient
le code et les données du thread. Mais le problème de base est
le même : dans le cas d'un thread « detaché », comment gerer
la durée de vie. (Dans le cas d'un thread « joinable », il
faut bien que l'appelant garde l'instance du thread jusqu'à la
join ; il pourrait donc aussi en être responsable de la durée
de vie.)

Dans mon propre code, je distingue bien entre les deux cas, dès
la création ; il n'y a même pas d'objet « thread » dans le
cas d'un thread détaché, seulement une fonction qu'on appelle
pour le lancer. Dans ses avatars actuels, c'est bien la fonction
qui démarre le thread (celle dont je passe l'adresse à
pthread_create) qui se charge du delete. À l'avenir, en
revanche, je passerais sans doute à la solution de Boost, avec
un objet fonctionnel qu'on copie. (Encore que... ça exige une
synchronisation supplémentaire au démarrage de chaque thread.)
Dans le cas des thread joinable, je fais à peu près comme la
solution proposée ici.

après le passage par une fonction système qui invoque une
fonction static de cet object, on récupère cette instance pour
appeler son 'run' l'arrêt du thread doit correspondre à la vie
de fin de cet objet (et vice versa d'ailleurs).



la mort brutale doit être gérée, comme tu le dis, via un catch
à plus haut niveau (run itself sûrement).



Aussi faut-il faire gaffe. Avec un compilateur (g++), un
pthread_cancel ou un pthread_exit n'invoque pas les destructeurs
sur la pile ; avec un autre (Sun CC), les destructeurs sont
appelés, comme pour une exception, mais on entre quand même pas
dans les blocs de catch. Pour l'instant, la seule solution qui
tient debut, à mon avis, c'est de les interdire.

(En ce qui concerne la normalisation future, le comité en
discute. Le problème de base, c'est que si on mappe une
cancelation à une exception, le thread peut l'attrapper et
continuer, comme rien n'était.)

> Il y a aussi certaines précautions à prendre comme mettre un
> catch à la base de ta fonction dans le thread (et aussi
> comment remonter l'erreur à l'objet appelant).



présumer de l'existence de cet appelant est risqué / douteux.
je broadcasterais plutôt un message.



Plus généralement, si on veut prévoir une possibilité d'arrêter
un thread de l'extérieur (ou un shutdown propre), il faut bien
un système de polling ; à cause des problèmes avec
pthread_cancel, l'arrête de chaque thread ne peut se faire que
depuis le thread. Ce qui veut dire que tous les lectures de
socket, etc., doivent être prémuni d'un timeout.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
James Kanze
Le #16790891
On Sep 11, 11:49 pm, Sylvain SF
James Kanze wrote on 11/09/2008 16:50:
>> oui, mes threads ont la durée de vie du programme.



> Dans quel cas, pas de problème:-). Tu les alloues dynamiquement,
> et tu n'en fais jamais de delete, et ça marche.



pourquoi pas:



void* POSIXThread::threadFunction(void* ref){
Runnable* runnable = static_cast<Runnable*>(ref);
if (runnable){
try {
runnable->run();
}
catch (...){
}
delete runnable
}
return NULL;
}



??



Pour commencer, parce que tu ne peux pas passer l'adresse d'une
fonction membre à pthread_create:-).

Plus généralement, ceci suppose que le runnable est toujours
alloué dynamiquement. Et qu'il ne contient pas de données qu'on
veut propager vers le thread appelant (suite à un join). Et
qu'on se contente d'ignore les exceptions. (« Sweep them under
the rug », dirait-on en anglais. Je ne connais pas d'équivalent
aussi imagé en français.)

purger proprement des threads à durée de vie courte ne me
parait pas tordu ?!



Il marche pour un cas de figure bien précis.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Publicité
Poster une réponse
Anonyme