Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

un vector de classe

14 réponses
Avatar
PasDeSpam
bonsoir,

j'ai une classe wrapper tres simple d'un mutex posix.

//h

class POSIXMutex
{

public:

POSIXMutex();
~POSIXMutex();

void lock();
void unlock();
void try_lock();



pthread_t getCurrent() const;
pthread_mutex_t& getMutex();


private:

pthread_mutex_t mutex;
pthread_t tid;

};

//cpp

POSIXMutex::POSIXMutex() {

pthread_mutexattr_t mutex_attr;
memset(&mutex_attr,0,sizeof(mutex_attr));

if (pthread_mutexattr_init(&mutex_attr) != 0)
exit(EXIT_FAILURE);


if(pthread_mutexattr_settype(&mutex_attr,PTHREAD_MUTEX_RECURSIVE) != 0)
return;

pthread_mutex_init(&mutex, &mutex_attr);

};


POSIXMutex::~POSIXMutex() {
pthread_mutex_destroy(&mutex);
};

.../....

maintenant j'aimerai avoir un std::vector<POSIXMutex> gates;

je ne me rappelle plus les conditions necessaires (objet copiable?) pour
un vector.

et dans mon cas que dois je faire?

merci

10 réponses

1 2
Avatar
Jean-Marc Bourguet
(Bruno Causse) writes:

maintenant j'aimerai avoir un std::vector<POSIXMutex> gates;

je ne me rappelle plus les conditions necessaires (objet copiable?) pour
un vector.



CopyConstructible et Assignable. Ce qu'a priori une classe mutex ne peut
pas etre (la premiere chose que je me suis dite en lisant ton message c'est
que tu avais oublie de mettre en prive ce constructeur et cet operateur).

et dans mon cas que dois je faire?



Te definir une classe qui n'a pas ces contraintes, revoir la raison pour
laquelle tu penses avoir besoin d'un tableau, utiliser directement un
tableau a la C sont trois options. Difficile de donner *la* bonne solution
quand on ne connait rien du probleme.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org
Avatar
James Kanze
On Sep 9, 8:33 am, Jean-Marc Bourguet wrote:
(Bruno Causse) writes:
> maintenant j'aimerai avoir un std::vector<POSIXMutex> gates;



> je ne me rappelle plus les conditions necessaires (objet
> copiable?) pour un vector.



CopyConstructible et Assignable. Ce qu'a priori une classe
mutex ne peut pas etre (la premiere chose que je me suis dite
en lisant ton message c'est que tu avais oublie de mettre en
prive ce constructeur et cet operateur).



Comme dans le bon vieux temps, tout problème se laisse résoudre
par un niveau d'indirection supérieur. Qu'il mette ses données
dans une struct à part, et que sa classe contient un
boost::shared_ptr ou quelque chose du genre à cette struct, et
il n'y a aucun problème. (C'est une solution, mais je ne dis pas
que c'est la bonne. Il faudrait en fait savoir pourquoi il veut
mettre les mutex dans une collection.)

--
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
Avatar
Fabien LE LEZ
On Tue, 9 Sep 2008 00:01:16 +0200, (Bruno Causse):

class POSIXMutex
{
[...]



Que cherches-tu à faire exactement ?

private:
pthread_mutex_t mutex;
pthread_t tid;



Ici, tu sembles lier un mutex à un thread. Ça me paraît un non-sens :
un mutex est lié à la variables (ou aux variables) qu'il protège, pas
à un thread particulier, puisque justement son rôle est de
synchroniser plusieurs thread.

maintenant j'aimerai avoir un std::vector<POSIXMutex> gates;



Là encore ça me paraît louche. Généralement, un seul mutex par
contexte est le plus qu'on arrive à gérer, et encore, sans toujours
pouvoir éviter les deadlocks.

je ne me rappelle plus les conditions necessaires (objet copiable?) pour
un vector.



Jean-Marc t'a répondu sur ce point. Mais l'idée générale est qu'on
stocke dans un vector<> des objets à sémantique de valeur.
Ce n'est généralement pas le cas d'un objet qui représente une entité
"réelle" du système, comme un thread, un mutex, une fenêtre, un
fichier (fstream), etc.
En revanche, un pointeur (pointeur nu ou shared_ptr<>) a une
sémantique de valeur, et peut être stocké dans un conteneur.
Avatar
Bruno Causse
"Jean-Marc Bourguet" a écrit dans le message de news:

(Bruno Causse) writes:

Te definir une classe qui n'a pas ces contraintes, revoir la raison pour
laquelle tu penses avoir besoin d'un tableau, utiliser directement un
tableau a la C sont trois options. Difficile de donner *la* bonne
solution
quand on ne connait rien du probleme.




j'ai une enorme table (2^24) de données (classe), que j'aimerai partager
entre plusieurs thread (lecture et ecriture)
plutot que bloquer l'acces a chaque operation (une seule porte d'entree),
j'ai prevu plusieurs portes (2^11).

l'index de la porte etant les 10 premiers bits de l index de la table.

la taille de la table et la quantité de porte doivent etre parametrable (a
la creation).

voila
Avatar
Bruno Causse
"Fabien LE LEZ" a écrit dans le message de news:

En revanche, un pointeur (pointeur nu ou shared_ptr<>) a une
sémantique de valeur, et peut être stocké dans un conteneur.




oui, c'est ma deuxieme idee

std::vector<POSIXMutex*> gatesPtr;
Avatar
Michael DOUBEZ
Bruno Causse a écrit :
"Jean-Marc Bourguet" a écrit dans le message de news:

(Bruno Causse) writes:

Te definir une classe qui n'a pas ces contraintes, revoir la raison pour
laquelle tu penses avoir besoin d'un tableau, utiliser directement un
tableau a la C sont trois options. Difficile de donner *la* bonne
solution
quand on ne connait rien du probleme.




j'ai une enorme table (2^24) de données (classe), que j'aimerai partager
entre plusieurs thread (lecture et ecriture)
plutot que bloquer l'acces a chaque operation (une seule porte d'entree),
j'ai prevu plusieurs portes (2^11).



Ça me parait beaucoup de mutex.


l'index de la porte etant les 10 premiers bits de l index de la table.



J'ai l'impression que tu t'apprête à créer un lock par ligne.

la taille de la table et la quantité de porte doivent etre parametrable (a
la creation).



Une technique pourrait être d'avoir un objet qui te donne le droit de
locker une portion de la table. Tu n'aurait donc pas un lock par ligne
mais un nombre de locks proportionnel au nombre d'accès simultanés à des
zones indépendantes. Et aussi ça te donne la possibilité de changer ta
politique de lock (ligne/colonnes/section...) et donc de tuner.


Si tu as beaucoup plus de lectures que d'écritures ça peut aussi valoir
le coup d'avoir un mécanisme de locks de lecture/locks d'écriture mais
ça il faut faire un benchmark.

--
Michael
Avatar
Bruno Causse
"Michael DOUBEZ" a écrit dans le message de news:
48c630a8$0$16587$
J'ai l'impression que tu t'apprête à créer un lock par ligne.

la taille de la table et la quantité de porte doivent etre parametrable
(a la creation).



Une technique pourrait être d'avoir un objet qui te donne le droit de
locker une portion de la table. Tu n'aurait donc pas un lock par ligne
mais un nombre de locks proportionnel au nombre d'accès simultanés à des
zones indépendantes. Et aussi ça te donne la possibilité de changer ta
politique de lock (ligne/colonnes/section...) et donc de tuner.


Si tu as beaucoup plus de lectures que d'écritures ça peut aussi valoir le
coup d'avoir un mécanisme de locks de lecture/locks d'écriture mais ça il
faut faire un benchmark.




je vois trois solutions a la synchronization de ma table.

1) un seul mutex : simplicité mais tres penalisant en perf
2) un mutex par entrée : simplicité mais tres penalisant en resource memoire
3) une table limité de mutex decoupant ma table en tranche : mix des deux
solutions precedentes

un ex sur un table de 16 elts (2^4) avec une table de mutex de 4 elts (2^2)

table[10] ------> 1010 donc lock du mutex 10 soit mutex[2]

les table[2], table[6], table[10] et table[14] sont inaccessibles tant que
le mutex est "locké".
Avatar
Michael DOUBEZ
Bruno Causse a écrit :
"Michael DOUBEZ" a écrit dans le message de news:
48c630a8$0$16587$
J'ai l'impression que tu t'apprête à créer un lock par ligne.

la taille de la table et la quantité de porte doivent etre parametrable
(a la creation).


Une technique pourrait être d'avoir un objet qui te donne le droit de
locker une portion de la table. Tu n'aurait donc pas un lock par ligne
mais un nombre de locks proportionnel au nombre d'accès simultanés à des
zones indépendantes. Et aussi ça te donne la possibilité de changer ta
politique de lock (ligne/colonnes/section...) et donc de tuner.


Si tu as beaucoup plus de lectures que d'écritures ça peut aussi valoir le
coup d'avoir un mécanisme de locks de lecture/locks d'écriture mais ça il
faut faire un benchmark.




je vois trois solutions a la synchronization de ma table.

1) un seul mutex : simplicité mais tres penalisant en perf
2) un mutex par entrée : simplicité mais tres penalisant en resource memoire
3) une table limité de mutex decoupant ma table en tranche : mix des deux
solutions precedentes



Tu peux aussi avoir une structure de control d'accès, protégée par un
mutex (exemple stupide: un bitfield).


un ex sur un table de 16 elts (2^4) avec une table de mutex de 4 elts (2^2)

table[10] ------> 1010 donc lock du mutex 10 soit mutex[2]

les table[2], table[6], table[10] et table[14] sont inaccessibles tant que
le mutex est "locké".



Ce que j'avais en tête était un système te permettant d'essayer
plusieurs politiques de lock:

//Base de donnée:
Database db;

//Accessor à la base de données avec politique:
DBAcces accessor(db,politique);

// Puis dans le code, en cas de lecture:
read_scoped_lock<Data> data(id,accessor);
// à ce moment là la partie de la base de donnée contenant id
// est lockée en lecture: i.e. les autres lecteurs peuvent
// avoir un lock jusqu'à ce qu'un ecrivain demande l'accès
return data->foo?data->bar:string();

// Puis dans le code, en cas d'écriture:
write_scoped_lock_lock<Data> data(id,accessor);
// à ce moment là la partie de la base de donnée contenant id
// est lockée en écriture: i.e. plus aucun lecteur ne peut lire
// et les autres lecteurs sont bloqués pour accéder à la zone de id
data->fooB;
data->bar="secret";
return;

Ensuite dans DBAcces, tu élabores la stratégie:

Exemple avec un seul mutex:

handle lock_read(long id)
{
//logger accès en lecture pour stats
//accès en lecture même qu'accès en écriture
return do_lock();
}

handle lock_write(long id)
{
//logger accès en écriture pour stats
return do_lock();
}

handle do_lock()
{
MutexRequire(db_mutex);
return db_mutex;
}


Exemple avec un mutex et une table d'accès

//table me permettant de déterminer les entrées lockées
Table table;
Mutex table_mutex;

handle do_lock(long id)
{
MutexRequire(table_mutex);
if(table[id].is_available)
{
table[id].is_availableúlse;
}
else
{
wait(id);
//attendre is_available relaché
}

return id;
}


Je dit pas que c'est trivial mais ça te permettra d'avoir des stats et
d'essayer plusieurs stratégies.
C'est peur être ce que tu avais prévu. Et l'indirection ne devrait pas
être trop couteuse. Avec un système de template pour une politique à la
compilation, le cout est en théorie nul (à l'exécution).

Personnellement, je commencerai avec un seul mutex puis en fonction des
stats je ferai évoluer la stratégie vers la différentiation
lecteur/écrivain ou une map qui associe id->mutex. Puis, si c'est pas
suffisant (trop d'accès simultanés pour recycler les mutex), je finirais
par un système de lock de zones qui est plus complexe. Mais tu as
surement plus de visibilité sur les besoins pour décider plus tôt.

--
Michael
Avatar
pjb
"Bruno Causse" writes:
1) un seul mutex : simplicité mais tres penalisant en perf



D'accord, mais il faut d'abord que ça marche.

2) un mutex par entrée : simplicité mais tres penalisant en resource memoire
3) une table limité de mutex decoupant ma table en tranche : mix des deux
solutions precedentes



Suppose qu'on mette un mutex par tranche de 100.

Fil 1:
table[1000]=table[0]+table[1];

Fil 2:
table[10]=table[1010]+table[1011];

Vérouillage mortel !


Si on en met un par entrée:

Fil 1:
s=table[0]+table[1];

Fil 2:
d=table[1]-table[0];

Vérouillage mortel !

Il suffit que deux fils accèdent à des tranches dans un ordre
différent pour finir en vérouillage mortel.


Donc ll faudra soit implémenter un système de transaction (avec
détection des verrouillages mortels et retour en arrière), soit ne
mettre qu'un verrou pour toute la table.

--
__Pascal Bourguignon__
Avatar
Michael DOUBEZ
Pascal J. Bourguignon a écrit :
"Bruno Causse" writes:
1) un seul mutex : simplicité mais tres penalisant en perf



D'accord, mais il faut d'abord que ça marche.

2) un mutex par entrée : simplicité mais tres penalisant en resource memoire
3) une table limité de mutex decoupant ma table en tranche : mix des deux
solutions precedentes





[snip]
Il suffit que deux fils accèdent à des tranches dans un ordre
différent pour finir en vérouillage mortel.



Sauf si l'accès au mutex ne dure que le temps d'avoir la valeur; cela
suppose que les valeurs sont décorrélées (on peut le supposer vu le
nombre apparent d'accès concurrents - 2**11!!!! :) ).


Donc ll faudra soit implémenter un système de transaction (avec
détection des verrouillages mortels et retour en arrière), soit ne
mettre qu'un verrou pour toute la table.



Une solution rapide est de déterminer les mutex à acquérir et le faire
dans un certain ordre (ascendant).

--
Michael
1 2