EnterCriticalSection

EnterCriticalSection - C++ - Programmation

Marsh Posté le 06-08-2009 à 08:25:39    

salut,

 

vous l'utilisez de quelle façon sur un objet, qui à un mutex déclaré en membre donnée :
1)
OBjet instance;
instance.AcquerirMutex()
istance.functionCritique()
insance.relaese

 

2)
OBjet::functionCritique()
{
AcuerirMutex()
//code
Release()
}

 


ça revient au même ?

 

Merci


Message édité par Glock 17Pro le 06-08-2009 à 08:27:54
Reply

Marsh Posté le 06-08-2009 à 08:25:39   

Reply

Marsh Posté le 06-08-2009 à 08:35:36    

je préfére 2/

Reply

Marsh Posté le 06-08-2009 à 08:53:30    

Ben si ta fonction critique est utilisée exclusivement dans des sections critiques, je pense qu'il est preferable de mettre le acquire/release dans la methode, donc je dirais 2.


---------------
Fresh
Reply

Marsh Posté le 06-08-2009 à 09:12:42    

d'acc merci

Reply

Marsh Posté le 15-08-2009 à 12:28:07    

et imaginons que la foncion retourne une référence sur une donné:
 
 
Object2* OBjet::functionCritique()
{
AcuerirMutex()
vector<Object2*>::iterator it_res=find(...)
Release()
return ptrObject2;
}
 
Là il vaut  mieux locker/unlocker à l'extérieur de la fonction ?

Reply

Marsh Posté le 15-08-2009 à 12:54:45    

la c'est un pointeur :€
sinon je vois pas la difference que ca fait :o

Reply

Marsh Posté le 15-08-2009 à 13:28:23    

La différence pour moi est la suivante :

 

Si juste aprés l'appel à Release() (et avant le return), le système switch sur un autre thread, et qu'une fonction Delete() soit appelée sur ptrObject2, in finé en retour de la fonction functionCritique() on se retrouve avec un ptrObject2 qui pointe sur une zone mémoire désaoullé, alors que si on lock en dehors de la fonction, on peut faire l'opération désirée sur le pointeur ptrObject2 retourné et relâcher le verrou qu'à partir de ce moment là. c'est très classique comme problème j'imagine...


Message édité par Glock 17Pro le 15-08-2009 à 13:41:29
Reply

Marsh Posté le 15-08-2009 à 20:01:45    

t'as aussi le droit d'utiliser pas des pointeurs :o

Reply

Marsh Posté le 16-08-2009 à 13:20:04    

ué mais c'est chaud niveau overhead que ça implique

Reply

Marsh Posté le 16-08-2009 à 14:45:05    

o_O ah bon ?
je vois pas en quoi

Reply

Marsh Posté le 16-08-2009 à 14:45:05   

Reply

Marsh Posté le 16-08-2009 à 15:54:36    

en faite il faut savoir que les vector de la stl créer une copie des objets stockées, si j'utilise des pointeurs la copie ne représentera que 4 octets , voilà pourquoi le fait de ne pas passer par des pointeurs induit un overhead

Reply

Marsh Posté le 16-08-2009 à 17:04:04    

Reply

Marsh Posté le 16-08-2009 à 18:05:45    

J'ajouterai qu'utiliser de la RAII pour le mutex peut être mieux, comme boost::mutex. Là si find() lance une exception, t'es mal, même si en l'occurence ça doit être une no-throw.
 
Joel : je crois que glock à qd même raison là. Quelquesoit le niveau d'optim du compilo, et même s'il peut éviter la copie dans vector::push_back et vector::insert, le vecteur est un container qui peut copier son contenu en s'agrandissant.
Utiliser un std::deque serait déjà mieux pour éviter les copies.
De plus, std::vector doit stocker l'information à un endrait précis de la mémoire (derrière le dernier élément en gros), alors que l'objet passé en argument de vector::push_back/vector::insert ne peut pas être à cet endroit, même si c'est une rvalue. Donc la copie est inévitable.
 
Ton article, interressant au passage, parle de la copy-elision autour d'un appel de fonction, pour dir que
T func( T t );,
T func( const T& t ); et
void func( const T& t_in, T& t_out );
 
avec un bon compilo, c'est à peut près pareil. En regardant l'implémentation de std::vector, aucun cas on ne peut éviter au moins copie, en pratique surement plusieurs. Avec std::deque, il n'y en a qu'une.
 
Comme je suis chaud, je tente un récapitulatif des copies, pour un container de taille N.
Je confonds volontairement copie et copy-construction.
 
std::vector< T > : N copies au mieux, N²/2 au pire
std::deque< T > : exactement N copies
std::vector< T* > : 0 copies, mais pas RAII
std::deque< T* > : 0 copies, mais pas RAII
std::deque< boost::optional< T > > : entre 0 et N copies selon T
 
pas RAII avec les pointeurs car le destructeur de std::vector ne libère pas la mémoire pointée. Il existe dans boost il me semble un vector< T* > qui libère la mémoire pointée pour retrouver du RAII. Ou alors utiliser std::vector< boost::shared_ptr< T > > mais c'est lourd.
std::deque< boost::optional< T > > permet d'éviter le niveau d'indirection mais la syntaxe de construction du nouvel objet est complexe pour éviter la copie. Si T ne peut être raisonablement contruit que par copie (une structure POD par exemple) il n'y a pas de gain.
 
De toute façon si tu doit utiliser des pointeurs vers les éléments de ton vecteur à différents endroit, vector n'est pas le bon choix, prends deque, qui lui ne déplace pas ses éléments.

Reply

Marsh Posté le 16-08-2009 à 18:20:05    

test avant de dire tu seras surpris ;)

 

Je veux dire regarde le code de glock :

 
Code :
  1. Object2* OBjet::functionCritique()
  2. {
  3. AcuerirMutex()
  4. vector<Object2*>::iterator it_res=find(...)
  5. Release()
  6. return ptrObject2;
  7. }
 

Tu mets

 
Code :
  1. Object2 OBjet::functionCritique()
  2. {
  3. AcuerirMutex()
  4. vector<Object2*>::iterator it_res=find(...)
  5. Release()
  6. return *ptrObject2;
  7. }
 

et hop tu copy-ellide. Y  a pas d'histoire de vecteur qui grandit. Ilr envoit une instance d'un element


Message édité par Joel F le 16-08-2009 à 18:24:21
Reply

Marsh Posté le 17-08-2009 à 10:09:50    

Vous me conseillerez quoi comme bon livre concernant le multi-threading ?

Reply

Marsh Posté le 17-08-2009 à 19:52:30    

J'aime bien http://www.manning.com/williams/
 
C'est spécifique C++1x, mais ça s'adapte très facilement à Boost.Thread. (C'est souvent uniquement une histoire d'include et de namespace).


Message édité par Evadream -jbd- le 17-08-2009 à 19:52:37
Reply

Marsh Posté le 17-08-2009 à 20:01:03    

je prends note, si d'autres personnes peuvent confirmer ajouter infirmer etc je suis preneur

Reply

Marsh Posté le 17-08-2009 à 22:32:41    


oui mais là il parle pas de non différence avec les compilo moderne entre vector<string> et vector<string*>  
ça parle de vector<string> f() et vector<string>& f()
 
ça me semble différent...

Reply

Marsh Posté le 17-08-2009 à 22:36:44    

ou alors si c'est la même chose je suis pas convaincu que mes objets copiés soient des R-value....

Reply

Marsh Posté le 17-08-2009 à 22:37:33    

ah ça y est, ça s'apelle c++1x maintenant...
 
@Joel : je suis d'accord avec toi sur ton bout de code, pour functionCritique(). Ce que je dit c'est que d'après son implémentation standard, vector<Object2> impose toujours au moins N copies à sa création. Ensuite en accès (comme ici) ça peut se copy-ellide en effet.
 
Je maintiens que vector<Object2>::push_back fait "normalement" au moins une copie, sauf si son argument est une r-value où là il y a peut-être ellision. De plus à tout ajout (push_back/insert) std::vector peut recopier ses élements, et là il n'y a pas d'élisions qui vaille, cf le standard.
 
Perso je reste méfiant quand au fait de choisir un coding-style qui suppose que le compilateur fera un super-boulot. Je préfère éviter à la main un maximum de copies, avec const& et plus tard const&&. Autrement dit, je n'utilise pas une implémentation qui copie sauf si le compilo arrive à ellide, je préfère une implémentation qui ne copie pas parce que c'est écrit comme ça. Je changerai d'avis qd l'ellision sera imposée par le standard.
 
std::string toto();
 
const std::string x = toto(); // ok si ellision
const std::string& y = toto(); // toujours ok
 
edit : && et non pas const&& qui n'est pas + utile que le const& classique.

Message cité 1 fois
Message édité par jesus_christ le 17-08-2009 à 22:40:57
Reply

Marsh Posté le 17-08-2009 à 22:39:57    

grillaid par Glock...
donc je rejoins Glock : l'article parle ellision, donc autour de petits bouts de code bien inlinable et eliidable, mais la représentation vector<T> et vector<T*> reste fondamentalement différente. Au niveau du RAII, et au niveau des copies, même si qlq unes peuvent être ellidées.

Reply

Marsh Posté le 18-08-2009 à 08:31:58    

jesus_christ a écrit :


@Joel : je suis d'accord avec toi sur ton bout de code, pour functionCritique(). Ce que je dit c'est que d'après son implémentation standard, vector<Object2> impose toujours au moins N copies à sa création. Ensuite en accès (comme ici) ça peut se copy-ellide en effet.


coolos quoi, je vois 0 push_back dans ce code. Je me doutes bien que push_back copie :E La il renvoit une valeur, c'est typquement un cas d'ellision possible.
Je vois pas pourquoi tu t'acharne su ton push_back, c'est clariement pas l'objet du code INITIAL posté apr Glock.

 

Quant au coding style, j'y peut rien si t'es coincé en gcc 2.95.2 ou en VC6 :o
L'exemple posté par dave dans les commentaires marche avec ellission sur gcc 4.1+ et MSVC 2005+ Apres moi ... je dis ça je dit rien.


Message édité par Joel F le 18-08-2009 à 08:32:54
Reply

Marsh Posté le 21-08-2009 à 22:05:49    

pour remplir son vecteur il doit bien faire des push_back/insert, donc la sémantique par copie compte aussi, mais c'est vrai que ce n'est pas le sujet.
 
J'ai une question au passage sur la durée de vie des rvalues. Si j'écris ça :
 
std::string f();
std::string x = f().c_str();
 
le c_str() est volontaire. f renvoie une string par valeur, en rvalue, et cette dernière est convertie en const char* pour être copiée à travers ce const char* dans x.
Mais question est : la rvalue de f() ne serait-elle pas détruite juste après l'appel de c_str() mais juste avant la copie, et ne copierait donc t-on pas depuis un char* invalide ?
Quand est détruite la rvalue ? juste après le ;, après la première évaluation (ici par c_str()) ou à la fin de scope ?
 
et cette syntaxe :
const std::string& x = f();
force-t-elle le compilo à laisser la rvalue "vivante" jusqu'à la fin du scope ou est-ce juste un moyen de garder une référence sur un object qui serait de toute façon resté sur la pile.
 
je fais assez souvent des appels cascadés sur des rvalue comme :
 
MonObject().GetAttribute().GetName().c_str()
 
et je ne sais pas trop si ici par exemple MonObject() est encore vivant quand c_str() est appelé.

Reply

Marsh Posté le 24-08-2009 à 09:38:38    

jesus_christ a écrit :


J'ai une question au passage sur la durée de vie des rvalues. Si j'écris ça :
 
std::string f();
std::string x = f().c_str();
 
le c_str() est volontaire. f renvoie une string par valeur, en rvalue, et cette dernière est convertie en const char* pour être copiée à travers ce const char* dans x.
Mais question est : la rvalue de f() ne serait-elle pas détruite juste après l'appel de c_str() mais juste avant la copie, et ne copierait donc t-on pas depuis un char* invalide ?
Quand est détruite la rvalue ? juste après le ;, après la première évaluation (ici par c_str()) ou à la fin de scope ?


EN c++1x, le constrcuteur de string par char**&& doit betement faire un swap, ce qui est suffisant.
 

jesus_christ a écrit :


et cette syntaxe :
const std::string& x = f();
force-t-elle le compilo à laisser la rvalue "vivante" jusqu'à la fin du scope ou est-ce juste un moyen de garder une référence sur un object qui serait de toute façon resté sur la pile.


http://herbsutter.spaces.live.com/ [...] !378.entry
 

jesus_christ a écrit :


je fais assez souvent des appels cascadés sur des rvalue comme :
 
MonObject().GetAttribute().GetName().c_str()
 
et je ne sais pas trop si ici par exemple MonObject() est encore vivant quand c_str() est appelé.


c_str() renvoit du caca des que la string qu'il reference est detruite, on ne doit sans servir que pr une utilsiation par const char* ou une recopie.

Reply

Marsh Posté le 26-08-2009 à 21:57:52    

Ué sympa ton article


---------------
.
Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

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