IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Mise à jour d'un Gestionnaire de Ressource


Sujet :

C++

  1. #1
    Futur Membre du Club
    Profil pro
    Inscrit en
    Août 2008
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2008
    Messages : 12
    Points : 8
    Points
    8
    Par défaut Mise à jour d'un Gestionnaire de Ressource
    Bonjour, je suis actuellement en train de réfléchir à la façon de faire un Gestionnaire de Ressource.
    Je me suis penché sur la façon de faire de Laurent dans son tutoriel sur la création d'un ResourceManager. J'ai retenu l'idée de passer par un MediaManager afin de gérer efficacement les différents type de média qui seront proposés dans mon application. Cependant, je reste perplexe sur la façon de réaliser mon ResourceManager proprement.

    Voici mon problème:
    J'utilise les shared_ptr que propose la bibliothèque Boost. J'ai donc une map définis comme ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    typedef boost::shared_ptr<Resource> ResourcePtr;
    std::map<std::string, ResourcePtr> ressources;
    Jusque là, tout va bien. J'ai réussi à gérer la demande de ressource, la création de la ressource si celle-ci n'est pas présente au sein de mon manager. Cependant, il se peut que la ressource ne soit plus utilisé à un instant t. Je voudrai donc la supprimer de ma map. Or, j'ai envie que cette tâche soit automatisé, et que ça soit le ResourceManager qui le fait automatiquement.

    Ma première solution est:
    Passer mon ResourceManager en singleton. Puis dans le destructeur d'une resource, je fais appel à une méthode de mon ResourceManager:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    void update(Resource* res) {
        rechercher la ressource dans la map
     
        SI compteur de la ressource EGAL A 1 ALORS
            supprimer ressource de la map
        FIN SI
    }
    En effet, si le compteur est égal à 1, cela veut dire qu'il n'est plus pointé que par mon shared_ptr de ma map. Et donc je peux le supprimer de la map.

    Soucis: Le Singleton.
    Je n'ai rien contre ce pattern, mais ici, je l'utilise uniquement pour avoir un accés global à mon ResourceManager, je pense pas que ça soit une bonne façon de faire.

    Deuxième Solution:
    Recréer une classe shared_ptr, celle-ci prendra en paramètre un foncteur. A ce foncteur dans son constructeur je lui passerai l'adresse du ResourceManager. Comme ça, à chaque fois qu'un shared_ptr est détruit, j'appelle ce foncteur. Puis le foncteur appellera la fonction update que je viens d'exposer plus haut.


    J'aimerai savoir s'il n'existerai pas un pattern qui résoudrait mon problème ? J'ai l'impression de me prendre la tête, mais j'aimerai faire un ResourceManager qui ne me prenne plus la tête par la suite et qui soit vraiment automatisé.

    En esperant avoir votre aide, je vous remercie d'avance.


    eXa

  2. #2
    Membre éclairé Avatar de HanLee
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    738
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Mai 2004
    Messages : 738
    Points : 871
    Points
    871

  3. #3
    Futur Membre du Club
    Profil pro
    Inscrit en
    Août 2008
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2008
    Messages : 12
    Points : 8
    Points
    8
    Par défaut
    C'est uniquement pour la destruction de l'objet pointé, pas du shared_ptr.
    La fonction ne sera appellée que lorsque delete sera appellé pour détruire l'objet pointé par le shared_ptr. :/

  4. #4
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Points : 4 625
    Points
    4 625
    Par défaut
    Ce qui tu essaies de recréer, c'est Boost.Flyweight en moins bien.
    Boost ftw

  5. #5
    Membre régulier

    Profil pro
    Étudiant
    Inscrit en
    Juin 2006
    Messages
    78
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2006
    Messages : 78
    Points : 105
    Points
    105
    Par défaut
    Tu peut garder un weak_ptr dans ton manager et fournir un shared_ptr. Le surcoût en mémoire est assez minime, et rien ne t'empêche de faire des passages dans la map de temps en temps pour vérifier leur validité et supprimer ceux qui ne le sont pas.

    Tant qu'un endroit de ton code possède la ressource (à travers un shared_ptr), tes weak_ptr restent valide et un autre endroit peut donc demander la même ressource. Si par contre, plus aucun shared_ptr ne pointe vers la ressource, alors le weak_ptr n'est plus valide, et tu re-créer la ressource.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    typedef boost::shared_ptr<Resource> ResourcePtr;
     
    class ResMng {
    public:
    	RessourcePtr create(const string & id) {
    		// lock ressources_mutex
    		std::map<std::string, RessourcePtr_weak>::iterator it =
    			ressources.find(id);
    		if (it != ressources.end()) {
    			RessourcePtr ret = it->second.lock();
    			if (ret) {
    				// unlock ressources_mutex
    				return ret;
    			}
    		}
    		RessourcePtr ret(new Ressource);
    		ressources[id] = RessourcePtr_weak(ret);
    		// unlock ressources_mutex
    		return ret;
    	}
     
    private:
    	typedef boost::weak_ptr<Ressource> RessourcePtr_weak;
    	std::map<std::string, RessourcePtr_weak> ressources;
    	// Mutex ressources_mutex;
    };
    "The worst errors I've ever seen do not came from no knowledge, but from having just the the right amount of it, too small to really understand what you're doing, but enough to think you did. That amount of knowledge, is evil."

  6. #6
    Futur Membre du Club
    Profil pro
    Inscrit en
    Août 2008
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2008
    Messages : 12
    Points : 8
    Points
    8
    Par défaut
    Je vous remercies pour vos deux réponses.
    Je vais retenir la solution de Aszarsha.
    Merci encore d'avoir prit la peine de ma répondre, sujet résolu.


    eXa

  7. #7
    Expert éminent

    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Février 2007
    Messages
    4 253
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Février 2007
    Messages : 4 253
    Points : 7 618
    Points
    7 618
    Billets dans le blog
    3
    Par défaut
    Tout dépend de ce que tu veux faire en resource-manager....
    Mais en général, le resource-manager à deux rôles:
    - Lire les resources (initialiser les objets Resource).
    - Gérer la durée de vie de ces resources.

    L'utilisation de shared_ptr est donc inutile (voire dangereuse). Le seul "propriétaire" de la resource c'est le manager... Si d'ailleurs c'est lui qui fait le new, c'est aussi lui qui doit faire le delete !

    Si cette unique argumentation ne vous convainc pas imaginons:
    (en bleu la version 'shared_ptr' coté client, en rouge la version 'pointeur' coté client, en noir, la version similaire).

    Toto a besoin de la resource Bobo, et la demande au manager.
    => le manager va créer/initialiser/lire la resource (10ms ?)
    Tata a besoin de la resource Bobo, et la demande au manager.
    => le manager va lui renvoyer la même resource.
    Toto n'a plus besoin de Bobo
    Tata n'a plus besoin de Bobo.
    => avec shared_ptr, destruction de la resource
    => avec pointeur simple, le manager peut décider de la destruction de la resoruce (besoin de mémoire), ou la garder en cache quelque part.
    Toto a a nouveau besoin de Bobo
    => le manager va devoir re-créer/initialiser/lire la resource apres avoir parcouru sa map, et s'être rendu compte que son weak_ptr n'est plus valide.
    => avec pointeur simple, le manager va juste renvoyer la resource si elle est en cache, ou... faire comme la première fois: création/initialisation/lecture... Dans tous les cas, c'est plus rapide que la version avec shared_ptr.


    Il y a plusieurs façons de réaliser ça:
    1. Comme le manager a une fonction "acquire" de resource, il a aussi une fonction "release". Tout est géré dans ces deux fonctions. Reste à coder un objet de smart pointer utilisant ces fonctions et le tour est joué.
    2. Les resources ont des fonction clientes "AddRef()" / "Release()" (mais non je ne fait pas du COM sans arrêt ), qui sont appelée par le client, une classe "Resource" de base implémente le compteur d'utilisateur et le pointeur vers le manager responsable de la resource, et appelle le manager dès que la resource est 'utilisée' ou 'plus utilisée'). Cette solution est un poil plus compliquée à programmer, mais bien plus souple (et rapide), permet d'avoir plusieurs managers en parallèle, ...
    N'oubliez pas de cliquer sur mais aussi sur si un commentaire vous a été utile !
    Et surtout

  8. #8
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Citation Envoyé par loufoque Voir le message
    Ce qui tu essaies de recréer, c'est Boost.Flyweight en moins bien.
    Ce n'est pas exactement la même chose tout de même. Par exemple, dans son cas, il accède aux ressources par nom. Avec un flyweight, on crée la ressource, et au moment où on l'affecte dans le flyweight, on se rend compte qu'elle existait déjà, et donc on référence l'existante et on oublie la nouvellement crée. C'est avant tout une optimisation de la mémoire, pas du temps de création. En outre, un flyweight n'est pas modifiable. Je ne sais pas si c'est le cas de ses ressources.
    Citation Envoyé par nicroman Voir le message
    Tout dépend de ce que tu veux faire en resource-manager....
    Mais en général, le resource-manager à deux rôles:
    - Lire les resources (initialiser les objets Resource).
    - Gérer la durée de vie de ces resources.

    L'utilisation de shared_ptr est donc inutile (voire dangereuse).
    Rien n'empêche un shared_ptr d'aider le ressourceManager à la seconde tâche. En particulier, il excelle pour gérer les références externes au manager sur une ressource gérée par le manager.
    Citation Envoyé par nicroman Voir le message
    => avec pointeur simple, le manager peut décider de la destruction de la resoruce (besoin de mémoire), ou la garder en cache quelque part.
    Toto a a nouveau besoin de Bobo
    => le manager va devoir re-créer/initialiser/lire la resource apres avoir parcouru sa map, et s'être rendu compte que son weak_ptr n'est plus valide.
    => avec pointeur simple, le manager va juste renvoyer la resource si elle est en cache, ou... faire comme la première fois: création/initialisation/lecture... Dans tous les cas, c'est plus rapide que la version avec shared_ptr.
    Une interface du manager retournant des shared_ptr n'empêche pas une variété de la gestion de la durée de vie.

    C'est par exemple le choix de l'utilisateur de détruire la ressource dès qu'elle n'est plus utilisée (voir premier post). Et un weak_ptr interne/shared_ptr externe réalise ça très bien.

    Et si on voulait qu'elle reste toujours en vie, ce qui serait un pattern d'utilisation possible, un shared_ptr interne et externe fournirait ce service.

    Et si on voulait une politique plus complexe, garder en interne un shared_ptr (pour contrôler semi manuellement la durée de vie) et un weak_ptr (pour l'accès) et utiliser des shared_ptrs en externe serait aussi une solution.

    Citation Envoyé par nicroman Voir le message
    Il y a plusieurs façons de réaliser ça:
    1. Comme le manager a une fonction "acquire" de resource, il a aussi une fonction "release". Tout est géré dans ces deux fonctions. Reste à coder un objet de smart pointer utilisant ces fonctions et le tour est joué.
    2. Les resources ont des fonction clientes "AddRef()" / "Release()" (mais non je ne fait pas du COM sans arrêt ), qui sont appelée par le client, une classe "Resource" de base implémente le compteur d'utilisateur et le pointeur vers le manager responsable de la resource, et appelle le manager dès que la resource est 'utilisée' ou 'plus utilisée'). Cette solution est un poil plus compliquée à programmer, mais bien plus souple (et rapide), permet d'avoir plusieurs managers en parallèle, ...
    Quand je vois acquire/release, ou addref/release, ou tout autre pattern basé sur l'utilisation de deux fonctions symétriques, je hurle de douleur en pensant à la charge que ça demande aux utilisateurs (en particulier en présence d'exceptions). Ce genre de fonctionnement est bien trop dangereux ! La solution en C++ pour gérer ça, c'est le RAII. Et le RAII associé à un comptage de référence peut s'écrire simplement shared_ptr (il peut aussi s'écrire autrement, mais il faudra me convaincre pourquoi. Par exemple, pour flyweight, il a réécrit son comptage de référence selon des principes proches d'un shared_ptr, mais c'est parce qu'il voulait présenter une interface de type valeur et non de type pointeur).
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Mise à jour d'une ressource
    Par jreeman dans le forum REST
    Réponses: 3
    Dernier message: 01/08/2011, 22h49
  2. Ressources non-mises à jour
    Par Kyliel dans le forum Maven
    Réponses: 4
    Dernier message: 20/01/2010, 12h58

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo