classes et threads

classes et threads - C++ - Programmation

Marsh Posté le 12-11-2002 à 10:43:08    

bonjour, je voudrais créer une classe, avec une routine en tache de fond dedans. je m'explique par un exemple :
 
class MyClass {
 public:
  MyClass(void) {/*Création d'un thread p-e ac CreateThread*/};
  ~MyClass(void) {/*destruction du thread*/};
 private:
  DWORD WINAPI ThreadEntry(LPVOID lpParameter){/*Entrée du thread*/return 0;}
}
 
mais voila, ne fonction 'WINAPI' ne fonctionne pas comme membre de classe, et puis je ne sais pas comment terminer proprement le thread lorsque la classe est détruite.
 
merci de votre aide


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 12-11-2002 à 10:43:08   

Reply

Marsh Posté le 12-11-2002 à 11:11:01    

moi j'utilise les fonctions de la librairie pthread ....
 
dans ma classe toto j'ai un membre de type pthread_t , lors de l'appel du constructeur de la calsse j'appelle la fonction pthread_create ou pthread_init (j'sais plus) pour creer/initialiser mon thread ...
mon destructeur de classe, lui, je le surcharge et j'appelle pthread_destroy sur le thread ...


---------------
"OCPLB : On Casse Pas Le Binôme, 'moiselle Jade, Carlson & Peters, page 823 !"
Reply

Marsh Posté le 12-11-2002 à 11:17:57    

euh ... oui ... je connais pas la lib pthread ... je peux la trouver ou ? parce que la je suis pas vraiment avancé


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 12-11-2002 à 11:24:56    

Tu es sous quel environment? (OS, Outil de development etc)


---------------
In tartiflette, we trust!
Reply

Marsh Posté le 12-11-2002 à 11:29:53    

@rf oui pardon
je suis sous windows (je cherche une solution qui fonctionne pour 95/98/me/nt4/2000/xp (enfin sous win32 quoi)), et je dev avec vc++ 6


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 12-11-2002 à 11:38:00    

BlackGoddess a écrit a écrit :

@rf oui pardon
je suis sous windows (je cherche une solution qui fonctionne pour 95/98/me/nt4/2000/xp (enfin sous win32 quoi)), et je dev avec vc++ 6




 
Tu es allérgique aux MFC ? (je te comprends), parce que sinon tu as tout ce qu'il faut avec les MFC pour créer très facilement des threads ( 2 solutions possible ).
Sinon tu as l'API Windows: CreateThread, auquel tu passes la fonction qui doit être executée dans le thread créé (voir MSDN pour les paramètres exactes). Cette fonction marche sur toutes les plateforms de Win9x , Me, NT 3.51, NT4, 2000, XP ...


---------------
In tartiflette, we trust!
Reply

Marsh Posté le 12-11-2002 à 11:50:21    

Une fonction "Thread" ne peut être déclarée comme membre d'une classe (comme tu as pu le constater). Cela est dû à la façon dont sont générées les fonctions membres en C++.
La seule solution qui existe est de la déclarer en static. Cependant il faut savoir que la fonction n'est alors pas reliée à une instance de la classe (pas de this par exemple). Il te faut donc envoyer le this en tant que paramètre. De même lors de la création de la thread, indique bien MyClass::ThreadEntry.
En fait cette solution ne présente que peu de différences avec l'utilisation d'une fonction globale. Elle peut être utile pour des raisons de clarté.


---------------
each day I don't die is cheating
Reply

Marsh Posté le 12-11-2002 à 11:50:37    

je suis en effet allergique aux MFC ... pour CreateThread, ce qui me bloque c l'erreur du compilo : un membre de classe ne peut pas être WINAPI alors que le paramètre de CreateThread pour le point d'entrée du thread l'oblige. Et aussi, je cherche comment terminer proprement mon thread a la destruction de la classe


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 12-11-2002 à 11:52:23    

par exemple un TerminateThread serait tres sale, mais je n'ai pas d'autres idées ...


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 12-11-2002 à 11:55:03    

Voir la reponse de "gatorette", c'est effet la solution.


---------------
In tartiflette, we trust!
Reply

Marsh Posté le 12-11-2002 à 11:55:03   

Reply

Marsh Posté le 12-11-2002 à 11:56:04    

ah oups oui dsl j'avais pas vu, il a posté pendant que j'ecrivais.


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 12-11-2002 à 12:00:00    

si on passe en paramètre this, le ThreadEntry pourra-t-il accéder aux memebres privés de l'objet ?


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 12-11-2002 à 12:10:55    

BlackGoddess a écrit a écrit :

si on passe en paramètre this, le ThreadEntry pourra-t-il accéder aux memebres privés de l'objet ?




 
Non impossible; tu ne peux pas accéder à des membres non statique d'un object dans une fonction statique; si tu réfléchis, le compilateur ne peu savoir à l'avance l'adresse de ton object (tu peux créér des centaines d'instance de ton object en même temps, hors, il n'existe qu'une seule implementation de ta fonction statique!!! )
 
Par contre, depuis ta classe, quand tu apelles le thread, tu peux par exemple créér en dynamique une petite structure que tu remplis avec les données de la classe necessaire au thread, et passer l'adresse de cette structure à la fonction CreateThread.


---------------
In tartiflette, we trust!
Reply

Marsh Posté le 12-11-2002 à 12:17:39    

ah !!! oui super ca je n'y avais pas pensé !
bin ca resoud aussi le pb de la destruction : ds le destructeur je peux mettre qqchose ds la structure (par exemple MustExit = TRUE) pour que comme ca la fonction du thread sache qu'il faut qu'elle arrete de travailler
 
merci bcp @ vous


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 12-11-2002 à 12:25:56    

You welcome!  :sol:


---------------
In tartiflette, we trust!
Reply

Marsh Posté le 12-11-2002 à 12:28:59    

ah oui, encore une question : vu que c'est la mm fonction qui est appelé a chaque fois, si on instancie 2 fois la classe, cela va créer 2 threads, or c'est 2 fois la mm fonction qui va etre appelé pour les threads. il ne risque pas d'y avoir de l'ecrasement de données ? je m'explique par un exemple :
 
DWORD EntryPoint(LPVOID lpParameter)
{
  DWORD dwTC = GetTickCount();
}
 
mettons par exemple qu'à la 1ere instanciation, la valeur récupérée ds dwTC soit 1000, puis on instancie une 2eme fois la classe, dwTC pour la 2eme instance sera par exemple de 3000. La question est : le 1000 de la 1ere instanciation va-t-il devenir un 3000 ?
 
si telle est le cas, je peux p-e me débrouiller avec des références :
 
DWORD EntryPoint(LPVOID lpParameter)
{
  DWORD* pdwTC = new DWORD;
  (*pdwTC) = GetTickCount();
}
 
ds ce cas, cela fonctionnera-t-il ?


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 12-11-2002 à 12:32:30    

j'ai oublié le static DWORD WINAPI MyClass::EntryPoint(LPVOID lpParameter)
à la place de DWORD EntryPoint(LPVOID lpParameter)
ne faites également pas attention à l'oubli de return 0; a la fin
c t juste pour l'exemple.


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 12-11-2002 à 12:37:50    

Il n'y a pas de problème à ce que la fonction soit appellée plusieurs fois; les problème arrivent seulement quand plusieurs thread accèdent à un même object; dans ce cas, il faut que tu utilises des system de synchronisation ( mutex, critical section ).


---------------
In tartiflette, we trust!
Reply

Marsh Posté le 12-11-2002 à 12:42:11    

ok, d'accord :)
 
justement, j'avais entendu parler de critical section, je me demandais a quoi ca servait. tu connaitrais par hasard des liens pour savoir comment s'en servir ?
 
sinon les mutex, j'en ai juste entendu parler comme moyen pour pas qu'on puisse lancer 2 fois l'app : elle crée un mutex au démarrage qd elle se lance, et si ca retourne AlreadyExist, alors c pas la 1ere instance de l'app. (c un peu restrinct par rapport à l'utilisté des mutex je suppose :p)


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 12-11-2002 à 12:48:21    

Voir:  
 
* InitializeCriticalSection() dans MSDN et fonctions associées, c'est une fonction du SDK Win32.  
 
* Voir le topic "Synchronisation overview" toujours dans MSDN
 
* Sinon: www.codeguru.com  il y a plein d'exemple, il suffit de chercher un peu
 
* www.thecodeproject.com, idem, plein d'exmples.
 
Avec ça, si tu t'en sors pas!  :D  
 


---------------
In tartiflette, we trust!
Reply

Marsh Posté le 12-11-2002 à 12:56:59    

YungMakko a écrit a écrit :

 
 
Non impossible; tu ne peux pas accéder à des membres non statique d'un object dans une fonction statique; si tu réfléchis, le compilateur ne peu savoir à l'avance l'adresse de ton object (tu peux créér des centaines d'instance de ton object en même temps, hors, il n'existe qu'une seule implementation de ta fonction statique!!! )
 
Par contre, depuis ta classe, quand tu apelles le thread, tu peux par exemple créér en dynamique une petite structure que tu remplis avec les données de la classe necessaire au thread, et passer l'adresse de cette structure à la fonction CreateThread.




 
Il me semble qu'en déclarant la fonction statique friend de la classe, alors cette fonction statique peut, en utilsant un pointeur sur l'instance, accéder à ses variables privées.
 
De plus, la structure en argument qui contient les données, outre le fait que ce n'est pas très pratique, interdit la consultation utlérieure des attributs de le l'instance ainsi que leur modification. Donc si une variable membre change, le thread ne pourra pas le savoir, de même qu'il ne pourra pas mettre à jour des attrubuts de cette instance (sauf bien sur si la classe dispose de méthodes pour cela, mais à ce moment là autant laisser les attributs en public...).

Reply

Marsh Posté le 12-11-2002 à 13:04:08    

SoWhatIn22 : si j'ai bien compris, si je déclare ma fonction en static friend DWORD WINAPI MyClass::ThreadEntry(LPVOID lpParameter) alors en passant comme argument this, je pourrais alors avoir acces aux membres privés en faisant : ((MyClass*)lpParamter)->MembrePrivé ? ai-je bien compris ?


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 12-11-2002 à 13:06:10    

pour la structure de données, si le thread recoit un pointeur vers cette structure il ne pourra pas changer les données de cette structure ???????


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 12-11-2002 à 13:26:26    

c bon g compris les critical sections avec les msdn, merci yungmakko  :)


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 12-11-2002 à 14:13:30    

SoWhatIn22 a écrit a écrit :

 
 
Il me semble qu'en déclarant la fonction statique friend de la classe, alors cette fonction statique peut, en utilsant un pointeur sur l'instance, accéder à ses variables privées.
 
De plus, la structure en argument qui contient les données, outre le fait que ce n'est pas très pratique, interdit la consultation utlérieure des attributs de le l'instance ainsi que leur modification. Donc si une variable membre change, le thread ne pourra pas le savoir, de même qu'il ne pourra pas mettre à jour des attrubuts de cette instance (sauf bien sur si la classe dispose de méthodes pour cela, mais à ce moment là autant laisser les attributs en public...).
 




 
Oui, en declarant la fonction statique de classe, ça fonctionne, le problème c'est que tous les membres de la classe doivent être statique s'y tu veux y acceder depuis la fonction statique! Ce qui n'est pas non plus vraiment pratique!!! (interet d'avoir une classe?)


Message édité par YungMakko le 12-11-2002 à 14:14:03

---------------
In tartiflette, we trust!
Reply

Marsh Posté le 12-11-2002 à 14:14:38    

BlackGoddess a écrit a écrit :

pour la structure de données, si le thread recoit un pointeur vers cette structure il ne pourra pas changer les données de cette structure ???????




 
Si sans problème; donc faire attention justement à cela!


---------------
In tartiflette, we trust!
Reply

Marsh Posté le 12-11-2002 à 14:28:05    

c justement la ou on peut mettre une critical section si g bien compris l'utilité du truc ? pour pas ke le thread veuille lire qqchose de la structre pendant que l'objet l'ecrit, ce genre de chose, c ca ?


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 12-11-2002 à 15:08:46    

BlackGoddess a écrit a écrit :

c justement la ou on peut mettre une critical section si g bien compris l'utilité du truc ? pour pas ke le thread veuille lire qqchose de la structure pendant que l'objet l'ecrit, ce genre de chose, c ca ?




 
Voila c'est ça, dans ton thread par exemple, quand tu accedes à une structure pour la lire ou la modifier:
 
...
Locker la criticale section
 
Lire/Modifier la structure (se debrouiller pour passer le minimum de temps ici pour ne pas trop bloquer les autres threads qui voudraient acceder aussi aux données; il peut être judicieux de stocker dans des variable locales les données necessaires, si les traitements sont long, et delocker immediatement la criticale section).
 
Delocker la criticale section
...


---------------
In tartiflette, we trust!
Reply

Marsh Posté le 12-11-2002 à 16:13:03    

super, merci, g appris plein de truc du ++ du c, et aussi ca faisait lgtps que les critical section me tapaient sur le système, merci bcp :)


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 12-11-2002 à 16:53:47    

YungMakko a écrit a écrit :

 
 
Oui, en declarant la fonction statique de classe, ça fonctionne, le problème c'est que tous les membres de la classe doivent être statique s'y tu veux y acceder depuis la fonction statique! Ce qui n'est pas non plus vraiment pratique!!! (interet d'avoir une classe?)




 
non. Si l'instance qui démarre le thread donne à ce thread un pointeur sur cette instance, il n'y a aucun soucis...
 
int MA_CLASSE::StartThread()
{
   // on crée le thread
   CreateThread( ..., start_routine, (void *)this, ... );
 
   /// bla bla bla...
   return 0;
}
 
/// fonction du thread
int start_routine( void * optarg )
{
   /// on récupère un pointeur sur l'instance
   MA_CLASSE * mon_instance;
   mon_instance = (MA_CLASSE *)optarg;
 
   /// et on fait ce que l'on veut avec l'instance
   mon_instance->min_attribut_prive0 = 7;
 
   /// etc...
 
   return 0;
}
 
 
 
 
je ne vois pas où est le problème la dedans... Une fonction statique ne peut manipuler que des attributs statiques de sa classe. Mais si on dispose d'un pointeur sur l'instance que l'on veut manipuler, alors on peut bien faire ce que l'on veut sur cette instance.
 
a+

Reply

Marsh Posté le 12-11-2002 à 17:01:07    


 
je ne vois pas où est le problème la dedans... Une fonction statique ne peut manipuler que des attributs statiques de sa classe. Mais si on dispose d'un pointeur sur l'instance que l'on veut manipuler, alors on peut bien faire ce que l'on veut sur cette instance.
 
a+
[/citation]
 
Dans le cas que tu as ecris en effet; mais si la fonction du thread est membre de la classe (quoi que je n'ai jamais essayé avec un Worker Thread, je bosse souvent avec des User Interface Thread), il est impossible d'accéder au membre non statique de classe depuis la fonction statique membre de cette même classe (ce qui est logique!) Le compilo te sors une erreur d'accès...


---------------
In tartiflette, we trust!
Reply

Marsh Posté le 13-11-2002 à 03:58:27    

Rhaa !
 
A part le nom imbriqué, une fonction statique de classe est une fonction oridinaire.
Si on lui transmet un pointeur sur un objet de cette classe, elle peut accéder à tous ses membres vu qu'elle fait partie de la classe.
Et si elle n'en fait pas partie, il suffit de la déclarer friend.

Code :
  1. class MyClass{
  2. static DWORD WINAPI MyClass::ThreadEntry(LPVOID);
  3. int MembrePrive;
  4. public:
  5. MyClass(){
  6.  ThreadEntry((LPVOID)this); //le cast n'est pas (void*) !
  7. }
  8. }
  9. static DWORD WINAPI MyClass::ThreadEntry(LPVOID lpParameter){
  10. MyClass* creator= (MyClass*)lpParamter; //constance éventuelle ignorée
  11. creator->MembrePrive; //accès ok
  12. MembrePrive; //pas bon, le this implicite n'existe pas
  13. }

C'est pas testé, mais je ne vois pas de raison que ça ne marche pas.
A moins que WINAPI exige un nommage C, incompatible avec l'imbrication de nom.
 
Attention, on perd l'information de constance du créateur !


---------------
Bricocheap: Montage de ventilo sur paté de mastic silicone
Reply

Marsh Posté le 13-11-2002 à 08:29:52    

nan mais déjà que c pas facile pour moi a comprendre, si en plus personne n'est d'accord ... :p
 
ca veut dire quoi :
"Attention, on perd l'information de constance du créateur !"
 
et aussi je comprends pas :  
//le cast n'est pas (void*) !
pour (LPVOID)this


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 13-11-2002 à 09:20:40    

Oui en effet en passant comme ça, ça marche tout à fait!
C'est pour acceder directement au membre que ça ne marche pas (evidemment!)
 
 
Si si, tout le monde est d'accord!   :D


---------------
In tartiflette, we trust!
Reply

Marsh Posté le 13-11-2002 à 09:43:56    

ah bien tlm est d'accord :)
 
mais g tjs pas compris l'histoire du créateur constant et du cast void* ... si qq1 pouvait m'expliquer :)


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 14-11-2002 à 03:04:05    

Citation :

Attention, on perd l'information de constance du créateur !

Code :
  1. const MyClass mc;
  2. //...
  3. static DWORD WINAPI MyClass::ThreadEntry(LPVOID lpParameter){
  4. MyClass* creator= (MyClass*)lpParamter; //constance éventuelle ignorée
  5. creator->threadisrunning= true; //PAN ! T'es mort.

Ça suffit comme explication ?
Il n'y a pas de problème si threadisrunning est qualifié "mutable".
 

Citation :

le cast n'est pas (void*) !

La fonction ThreadEntry prend un LPVOID comme argument.

Code :
  1. ThreadEntry((LPVOID)this); //au lieu d'écrire ça...
  2. ThreadEntry((void* )this); //...certains écrivent ça.

"Certains" fainéants ou qui se croient malins vont avoir une surprise le jour du passage au 64 bits:
void* sera naturellement un pointeur 64 bits.
LPVOID restera à sa définition historique de 32 bits.
 
Voilà, c'est juste un excellent moyen de se tirer à retardement un obus dans le pied.


---------------
Bricocheap: Montage de ventilo sur paté de mastic silicone
Reply

Marsh Posté le 14-11-2002 à 09:14:12    

k, g compris l'histoire du void*, mais pour la constance du créateur, si g bien compris, c si on délcare de pointeur vers l'objet en constante, cela ne sera pas repercuté sur TreadEntry, c ca ?


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 15-11-2002 à 03:00:51    

Oui.
Le pointeur LPVOID oblige à utiliser une cast, et de ce fait ThreadEntry ignore si l'objet est constant ou pas.
C'est tout.


---------------
Bricocheap: Montage de ventilo sur paté de mastic silicone
Reply

Marsh Posté le 15-11-2002 à 08:03:04    

bin merci me voila fortement eclairé now :)
 
merci bcp @ tous :)


---------------
-( BlackGoddess )-
Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed