8
votes

Comment éviter de fuir les poignées lors de l'invocation de l'interface utilisateur de System.threading.Timer?

Il semble que l'appelant soit appelé sur un contrôle WinForms dans un rappel à partir d'un système.Threading.Timer fuit des poignées jusqu'à ce que la minuterie soit disposée. Quelqu'un a-t-il une idée de la façon de travailler autour de cela? J'ai besoin de sonder une valeur chaque seconde et de mettre à jour l'interface utilisateur en conséquence.

Je l'ai essayé dans un projet de test pour vous assurer que c'était bien la cause de la fuite, ce qui est simplement le suivant: P>

    System.Threading.Timer timer;
    public Form1()
    {
        InitializeComponent();
        timer = new System.Threading.Timer(new System.Threading.TimerCallback(DoStuff), null, 0, 500);
    }
    void DoStuff(object o)
    {
        this.Invoke(new Action(() => this.Text = "hello world"));
    }


2 commentaires

Règle n ° 1 Chaque C ++ adhère à: Chaque nouveau doit être suivi de Supprimer!


Oui, je me demandais comment cela a fonctionné, en mettant un délégué là-bas pour chaque rappel semble être créé les poignées.


4 Réponses :


2
votes

y a-t-il une raison pour laquelle vous ne pouvez pas utiliser de System.Windows.Forms.Timer ici? Si la minuterie est liée à cette forme, vous n'avez même pas besoin d'invoquer.


4 commentaires

Dans le code actuel, la minuterie n'est pas sous la forme, elle est dans le présentateur appelant à la vue. J'ai essayé de changer d'un système.windows.forms.ticer, mais il ne faisait pas tirer pour une raison quelconque et je n'ai pas dépensé trop de temps à essayer de comprendre pourquoi et figuré cela avait quelque chose à voir avec cela ne pas être dans une classe dérivée du contrôle


Je recommanderais vraiment d'utiliser Windows.Forms.Timer pour cela (pas que ce ne serait pas agréable de comprendre la fuite de la poignée!). Avez-vous défini la propriété activée sur la minuterie lorsque vous l'avez essayé? Je ne vois aucune raison pour laquelle il aurait besoin d'être créé par un code dans une classe dérivée du contrôle - comment saillirait-il, pour un début?


J'ai défini un intervalle, a donné à la tick événement un gestionnaire et appelé Démarrer () dessus, mais le point de pause sur le gestionnaire d'événements n'a jamais été touché. Quoi qu'il en soit, on dirait qu'il n'y avait pas eu de fuite de poignée, tant que le collecteur des ordures prend un certain temps pour se déplacer. Voir ma propre réponse.


Il ne fonctionnera pas car le thread contenant n'est pas pompant. Il doit appartenir à la forme.



0
votes

intéressant - ce n'est pas une réponse, mais basé sur le commentaire d'Andrei, j'aurais pensé que cela ne manquerait pas de la même manière, mais il fait des manches de fuite au même tarif que l'OP mentionné.

System.Threading.Timer timer;
    public Form2()
    {
        InitializeComponent();

    }

    private void UpdateFormTextCallback()
    {
        this.Text = "Hello World!";
    }

    private Action UpdateFormText;

    private void DoStuff(object value)
    {
        this.Invoke(UpdateFormText);
    }

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        timer = new System.Threading.Timer(new TimerCallback(DoStuff), null, 0, 500);
        UpdateFormText = new Action(UpdateFormTextCallback);
    }


1 commentaires

Cela pourrait-il être un problème de maréchalation de compartiment sans nettoyage?



6
votes

Invoquer des actes comme une paire de beginInvoke / endinvoke en ce sens qu'elle publie le message au fil de l'interface utilisateur, crée une poignée et attend la poignée pour déterminer lorsque la méthode invoquée est terminée. C'est cette poignée qui "fuite". Vous pouvez voir que ceux-ci sont des événements sans nom à l'aide de Explorateur de processus pour surveiller les poignées tandis que l'application est en cours d'exécution.

Si IAsyncResult était isisposable, l'élimination de l'objet s'occuperait de nettoyer la poignée. Comme ce n'est pas le cas, les poignées sont nettoyées lorsque le collecteur des ordures s'exécute et appelle le finalisateur de l'objet iAsyncResult. Vous pouvez le voir en ajoutant un GC.Collect () après tous les 20 appels vers Dostuff - le nombre de poignées diminue toutes les 20 secondes. Bien sûr, "résoudre" le problème en ajoutant des appels à gc.Collect () est le mauvais moyen pour résoudre le problème; Laissez le collecteur des ordures faire son propre travail.

Si vous ne devez pas besoin l'appel d'invocation pour être synchrone, utilisez Begkinvoke au lieu d'appeler et n'appelle pas endinvoke; Le résultat final fera la même chose mais aucune poignée ne sera créée ou «fuite».


2 commentaires

J'ai remarqué que cela se sont partis dessus et je viens donc de le laisser à cela, mais merci pour l'explication.


Joli. Merci pour la réponse détaillée. Bon à savoir.



2
votes

D'accord, je l'ai donné un peu plus de temps et on dirait que cela ne fuit pas les poignées qui ne fuient pas, c'est juste la nature indéterminée du collectionneur des ordures. J'ai heurté la route jusqu'à 10 ms par coche et cela grimperait très vite et 30 secondes plus tard, tomberait.

Pour confirmer la théorie, j'ai appelé manuellement gc.collect () sur chaque rappel (ne le faites pas dans de vrais projets, il s'agissait simplement de tester, il y a de nombreux articles sur la raison pour laquelle c'est une mauvaise idée) et le nombre de personnes comptent était stable.


0 commentaires