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 :

Dipose et object non managé.


Sujet :

C#

  1. #1
    Rédacteur
    Avatar de Erakis
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2003
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : Canada

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 523
    Par défaut Dipose et object non managé.
    Bonjour à tous,

    Qu'entend-on nous par object non managé. Si j'ai créé une DLL en MFC et que j'y fasse appel avec des fonctions du genre Start et Stop.
    Mettons que cette DLL fait que jouer un son wave dans un buffer static encapsulé dans la DLL.

    Il est impératif que la méthode Stop soit appelée lors de la destruction de l'objet, aussi elle devrait l'être si l'utilisateur appel Dispose ou encore Stop.

    De quelle façon doit-on implémenter le dispose à ce moment là ?

    Façon #1
    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
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
     
    public class MyMFCWrapper : IDisposable
    {
        [DllImport(DllPath, EntryPoint = "Start")]
        public static extern int MFCStart();
     
        [DllImport(DllPath, EntryPoint = "Stop")]
        public static extern int MFCStart();
     
        private bool disposed = false;
     
        public MyMFCWrapper ()
        {
           ...
        }
     
        public int Start()
        { 
           return MFCStart();
        }
     
        public int Stop()
        { 
           return MFCStop();
        }
     
        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                     Stop();
                }
     
                disposed = true;
            }
        }
     
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }
    Façon #2
    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
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
     
    public class MyMFCWrapper : IDisposable
    {
        [DllImport(DllPath, EntryPoint = "Start")]
        public static extern int MFCStart();
     
        [DllImport(DllPath, EntryPoint = "Stop")]
        public static extern int MFCStart();
     
        private bool disposed = false;
     
        public MyMFCWrapper ()
        {
           ...
        }
     
        public int Start()
        { 
           return MFCStart();
        }
     
        public int Stop()
        { 
           return MFCStop();
        }
     
        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                     ....
                }
     
                Stop();
                disposed = true;
            }
        }
     
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
     
        ~ MyMFCWrapper()
        {
            Dispose(false);
        }
    }
    En fait ce qui me me mêle le plus, ce n'est pas de comprendre comment fonctionne le Dispose pattern mais plutôt de savoir QUAND exactement est-il préférable d'implémenter un finalizer (destructeur). On dit que c'est nécessaire lorsqu'on travail avec des ojbect NON-MANAGÉ. Dans ce cas-ci, cette DLL est bien un objet NON-MANAGÉ, cependant elle n'alloue pas vraiment de mémoire ?!?

    PS : D'autant plus que j'ai lu à plusieurs reprise qu'implémenter un finializer inutilement peut entraîner des pertes de performances...

    Je vous remerci d'avance pour votre aide.

  2. #2
    Membre émérite
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    547
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 547
    Par défaut
    Bonjour Erakis,

    Tout d'abord, concernant la necessité de la reecriture du finalizer sur des classes manipulant du non-managé, oui c'est essentiel. Imaginons, que tu oublies d'appeler un Dispose (l'appel à Dispose est fortement recommandé mais dans l'absolu, on _pourrait_ s'en passer), si le finalizer ne nettoie pas les ressources non gérés, tu vas te retrouver avec des handles qui vont se ballader (pour coller à l'exemple, si l'objet est deifinitivement detruit, qui va appeler MFCStop() ?)

    Pour l'affaire des performances, c'est vrai (et faux en meme temps ! =p). En fait, un objet qui override un finalizer survit à une collecte supplementaire : Quand un objet va etre collecté, si il a un finalizer, le GC va appelé ce dernier et donc ne pas collecté l'objet en question, pour note un objet n'a de finalizer que si il a été explicitement implementé (en gros pas de finalizer par defaut). Maintenant pour resoudre ce probleme, dans les classes manipulant du non-géré (et donc obligé de reecrire un finalizer), on utilise la methode GC.SuppressFinalize(this) qui va preciser au GC que le menage a été fait et que finalizer ou pas, pas besoin de l'appeler.

    Donc En implementant le pattern Disposable comme tu l'as fait dans le deuxieme exemple, tu es assuré que tes ressources non-gérées seront libérées, et tu n'emcombres pas le GC d'un cycle supplémentaire pour appeler un finalizer qui ne servirait à rien si Dispose() a été appelé précédemment.

    Voila, je ne sais pas si j'ai etait clair, mais l'idée à retenir :
    - Non managé = Finalizer
    - Si tu supprimes l'appel du finalizer sur un dispose explicite, tu n'auras pas de probleme de perf.

  3. #3
    Rédacteur
    Avatar de Erakis
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2003
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : Canada

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 523
    Par défaut
    C'est que je me suis basé sur cet acticle très bien expliqué : Code Project

    Finalization increases the cost and duration of your object's lifetime since each finalizable object must be placed on the finalization queue when it is allocated. This happens even if the finalizer is never called. This has the side effect of making the GC work harder to dispose of your object and causes it be kept alive longer.
    Ou encore ce site : Microsoft MSDN

    Remember that executing a Finalize method is costly to performance.
    Je développe un application avec Compact Frameowork. J'ai un fichier de configuration à transférer du serveur (PC) à mon Pocket PC. Ce fichier fait environ 4 Mo.

    Lorsque mon application Pocket PC est déconnecté du serveur pour une raisons X, elle doit se reconnecter au serveur et par la même occasion redemandé le fichier de 4Mo.

    Bref, lorsque le Pocket PC détecte que la connexion à été coupé, il affecte NULL à l'objet qui stock le contenu de 4Mo (une classe). Par contre, j'ai un exception OutOfMemoryException qui surviennent lorsque je ré-alloue la mémoire pour accueuillir mon objet de 4Mo.

    Je ne sais plus vraiment quoi faire...

  4. #4
    Membre émérite
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    547
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 547
    Par défaut
    Pour la citation, c'est en effet vrai, mais justement le fait de supprimer cette finalization si l'objet a été disposé va venir atenuer ce surcout (totalement en ce qui concerne la durée de vie, peut etre pas pour le "cout", mais ca doit rester negligeable).

    Quand tu parles de reallocation, j'imagine que tu parles d'un appel à ta dll MFC alouant de la memoire. Est ce que ca marche si le processus se passe correctement ?

    NB : je ne sais pas trop si je pourrais t'aider, n'ayant jamais utilisé le Fx compact.

  5. #5
    Rédacteur
    Avatar de Erakis
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2003
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : Canada

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 523
    Par défaut
    Non le Dispose était en fait une question par pure curiosité, mais qui peut toutefois rejoint mon problème.

    J'ai retiré ma DLL (MFC) du projet pour être sur que ce n'est pas elle qui génère le problème de OutOfMemoryException. Et comme je m'y attendait, ce n'est pas elle qui cause cet exception.

    Donc, cet exception est bel et bien généré à cause d'un manque de mémoire physique sur le Pocket PC. Ce qui est emmerdant, c'est que, si cette application serait construite en MFC par exemple alors je pourrais faire ;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    delete MyBigObject;
    Et la mémoire serait alors libérée IMMÉDIATEMENT. Au lieu de ça, avec le .NET mon objet est libéré que lorsque le GC décide de le faire. Ce qui fait qu'entre le moment où j'ai libéré ma mémoire et le moment où je la ré-alloue, je peux me retrouver avec le DOUBLE de mémoire ALLOUÉ, le temps que l'ancienne soit DÉFINITIVEMENT libéré par le GC.

    Ce problème pourrait aussi bien se produire sur un PC avec le .NET Framework. Alors c'est pour cette raison que j'ai posté mon problème ici.

    Je ne sais vraiment plus comment régler ce problème, j'ai coupé par tout le moyens possible mais la...

    Si quelqu'un a des sugestions, je suis ouvert à tout !

  6. #6
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2008
    Messages
    91
    Détails du profil
    Informations personnelles :
    Âge : 52
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : Avril 2008
    Messages : 91
    Par défaut
    je sais pas si sa peut t'aider, mais en travaillant avec Microsoft.Office.Interop.Outlook j'ai été confronté a un problème de libération des objets com

    j'ai utiliser ce code la :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    Marshal.releaseComObjet(InstanceObjetCom)
    InstanceObjetCom = null
    Et mon problème étais Regler

  7. #7
    Rédacteur
    Avatar de Erakis
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2003
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : Canada

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 523
    Par défaut
    Malheureusement ce n'est pas un problème avec un COM. C'est avec un Activator.CreateInstance qui me génère cette erreur

    Aussi, j'ai un dernière question en rapport avec le Dispose
    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
    27
    28
    29
    30
    31
     
    private disposed = false;
    private TCPClient client;
    public void Close()
    { 
       this.Dispose(); 
    }
     
    public void Dispose() 
    {
          this.Dispose(true);
          GC.SuppressFinalize(this);
    }
     
    protected virtual void Dispose(bool disposing) 
    { 
          if (!disposed)
          {
              if (disposing) 
              { 
                    if (client != null) 
                         client.Close();
              }
              disposed = true;
          }
    }
     
    ~Client()
    { 
       Dispose(false); 
    }
    Prenons l'exemple suivant :
    Si l'utilisateur n'appel pas Dispose à la fin du programme alors le GC appelera le destructeur un un peu plus tard. Mais lorsque le destructeur sera appelé, l'argument FALSE sera passé à la Dispose, de ce fait cette ligne-ci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    if (client != null) 
       client.Close();
    ne sera jamais EXÉCUTÉ ??? Le socket client restera ouvert...

  8. #8
    Membre émérite
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    547
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 547
    Par défaut
    TCPClient est une classe managé, donc il sera forcement disposé et detruit par le GC dans un laps de temps inconnu. Et de toutes facons, il est lourdement deconseillé de toucher à du managé depuis un finalizer : de par le mode de fonctionnement meme du GC, il se peut qu'un objet est deja etait finalisé (l'ordre de finalisation des objets est indefini), et dans ce cas, y acceder serait source d'erreur.

    Donc non pas de probleme avec le code ci dessus, du moins pas plus que de laisser trainer hors-scope un FileStream sans le disposer; il sera balayé à un moment, mais tu perds l'avantage du determinisme de l'appel explicite à dispose, ce qui est l'enorme interet de ce pattern.

  9. #9
    Rédacteur
    Avatar de Erakis
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2003
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : Canada

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 523
    Par défaut
    Je comprend ce que vous dites, imaginons que nous développions une classe et qu'elle possède des membres étant managé (comme l'exemple précédant), il faut se dire que si l'utlisateur ne fait pas appel à la méthode Close, alors les membre managé seront LIBÉRÉ correctement par le GC ?

    Prenons par exemple la classe UdpClient (Déssamblé par Reflector) :
    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
     
    public void Close()
    {
        this.FreeResources();
        GC.SuppressFinalize(this);
    }
     
    private void FreeResources()
    {
        if (!this.m_CleanedUp)
        {
            Socket client = this.Client;
            if (client != null)
            {
                client.InternalShutdown(SocketShutdown.Both);
                client.Close();
                this.Client = null;
            }
            this.m_CleanedUp = true;
        }
    }
     
    void IDisposable.Dispose()
    {
        this.Close();
    }
    Ici, si l'utilisateur n'appel pas CLOSE alors le socket ne sera pas fermé. Et comme il n'y a pas de FINALIZER alors... il faudra attendre que le GC appel le finalizer de l'object Socket.
    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
    27
    28
    29
    30
    31
    32
    33
    34
     
    ~Socket()
    {
        this.Dispose(false);
    }
     
    protected virtual void Dispose(bool disposing)
    {
        if (Interlocked.Increment(ref this.m_IntCleanedUp) == 1)
        {
            AutoResetEvent asyncEvent = this.m_AsyncEvent;
            if (this.m_Handle != SocketErrors.InvalidSocketIntPtr)
            {
                this.InternalSetBlocking(true);
                int num = UnsafeNclNativeMethods.OSSOCK.closesocket(this.m_Handle);
                this.m_Handle = SocketErrors.InvalidSocketIntPtr;
                this.SetToDisconnected();
            }
            if (asyncEvent != null)
            {
                if (disposing)
                {
                    try
                    {
                        asyncEvent.Set();
                    }
                    catch
                    {
                    }
                }
                this.m_AsyncEvent = null;
            }
        }
    }
    Par contre, ici dans le Socket on voit très bien que même si disposing est à false, la connexion physique sera FERMÉ.

    C'est quand même un peu fou. Il faut faire confiance au objet managé de façon aveugle. Une chance qu'on a le Reflector

  10. #10
    Membre émérite
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    547
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 547
    Par défaut
    Citation Envoyé par Erakis Voir le message
    Je comprend ce que vous dites, imaginons que nous développions une classe et qu'elle possède des membres étant managé (comme l'exemple précédant), il faut se dire que si l'utlisateur ne fait pas appel à la méthode Close, alors les membre managé seront LIBÉRÉ correctement par le GC ?
    Voila. Un membre managé est assuré d'avoir sa destruction pris en charge par le GC.

    Pour l'exemple sur Socket, c'est en effet etrange, mais je t'avouerai que n'ayant jamais eu de probleme avec, je fais confiance aux devs de Microsoft qui ont pondu çà. =)

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

Discussions similaires

  1. convertir un type non managé en type managé.
    Par poporiding dans le forum MFC
    Réponses: 6
    Dernier message: 22/05/2006, 11h49
  2. Réponses: 12
    Dernier message: 30/01/2006, 22h13
  3. [C++] Appel via paramètres non managés
    Par JulienDuSud dans le forum Framework .NET
    Réponses: 4
    Dernier message: 28/12/2005, 11h42
  4. code non managé avec interface managée ...
    Par izbad dans le forum MFC
    Réponses: 6
    Dernier message: 19/12/2005, 17h36

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