32 votes

C #: Dois-je disposer d'un BackgroundWorker créé à l'exécution?

J'ai généralement un code comme celui-ci sur un formulaire:

     private void PerformLongRunningOperation()
    {
        BackgroundWorker worker = new BackgroundWorker();

        worker.DoWork += delegate
        {
            // perform long running operation here
        };

        worker.RunWorkerAsync();
    }
 

Cela signifie que je ne dispose pas du BackgroundWorker , alors que si je l'avais ajouté par le concepteur de formulaire, je pense qu'il serait éliminé.

Cela causera-t-il des problèmes? Est-il plus correct de déclarer un niveau de module _saveWorker , puis d'appeler Dispose depuis la méthode de disposition du formulaire?

33voto

Simon P Stevens Points 17536

Oui, vous devez disposer de l'arrière-plan travailleur.

Vous pouvez trouver plus facile à utiliser ThreadPool.QueueUserWorkItem(...) qui ne nécessite pas de nettoyer après.


Plus de détails sur pourquoi vous devriez toujours faire appel Dispose():

Bien que si vous regardez dans la classe BackgroundWorker il ne fait aucun fil nettoyer dans sa méthode dispose, il est toujours important de Disposer à cause de l'effet de la classe a sur le garbage collector.

Classes avec les finaliseurs ne sont pas GCed immédiatement. Ils sont conservés et ajouté à l'outil de finalisation de la file d'attente. Le finaliseur thread s'exécute, (qui suit le schéma habituel des appels en disposer). Cela signifie que l'objet de survivre en GC de génération 1. Et gen 1 collections sont beaucoup plus rares que les collections de la génération 0, de sorte que vous objet autour de bâtons dans la mémoire de beaucoup plus de temps.

Toutefois, si vous appelez dispose(), l'objet ne sera pas ajouté à la file d'attente de finalisation, est donc libre d'être nettoyée.

Ce n'est pas vraiment un gros problème, mais si vous êtes à la création de beaucoup d'entre eux vous finirez par utiliser plus de mémoire que nécessaire. Il devrait vraiment être considéré comme une bonne pratique de toujours faire appel à disposer sur les objets qui ont une méthode dispose.

Je suppose donc que, dans l'ensemble, il n'est pas à 100% à un dur et rapide exigence. Votre application ne sont pas exploser (ou même une fuite de mémoire) si vous ne faites pas appel Dispose(), mais dans certaines circonstances, il peut avoir des effets négatifs. L'arrière-plan travailleur a été conçu pour une utilisation en tant que WinForms composant afin de l'utiliser de cette façon, si vous avez des exigences différentes et vous ne voulez pas l'utiliser comme un WinForms composant, ne l'utilisez pas, utiliser le bon outil pour le travail, comme le pool de threads.

16voto

Neil Barnwell Points 20731

Le défi est de s'assurer que vous ne jetez l' BackgroundWorker après c'est fini de s'exécuter. Vous ne pouvez pas le faire dans l' Completed événement, parce que l'événement est déclenché par le BackgroundWorker lui-même.

BackgroundWorker est vraiment destiné à être utilisé comme un composant sur un WinForms forme, alors je vous conseille de le faire, ou de passer à quelque chose comme Thread.QueueUserWorkItem. Cela permettra d'utiliser un thread du pool de thread, et ne nécessite aucun nettoyage quand il est terminé.

7voto

Wayne Points 3098

À mon avis, en général, si c'est IDisposable, ce devrait être Dispose () d quand vous en aurez fini. Même si techniquement, l'implémentation actuelle de BackgroundWorker ne nécessite pas d'être éliminée, vous ne voulez pas être surpris par les implémentations internes ultérieures.

6voto

Henk Holterman Points 153608

Je n'aurais pas pris la peine, la seule ressource de la Bgw pouvais tenir le Fil et si vous n'avez pas une boucle infinie dans votre délégué alors vous êtes très bien.

BackgroundWorker hérite IDisposable() de Composant, mais n'en a pas vraiment besoin.

Le comparer à pousser votre méthode directement sur le pool de threads. Vous n'avez pas (ne peuvent pas) Disposer fils, certaily pas ceux de la Piscine.

Mais si votre échantillon est complet dans le sens que vous n'êtes pas en utilisant l'événement Terminé ou en Cours/Annuler fonctionnalités dont vous pourriez tout aussi bien utiliser ThreadPool.QueueUserWorkItem().

4voto

jomtois Points 845

Pourquoi ne pas envelopper dans une instruction d'utilisation? Pas beaucoup d'effort supplémentaire et vous obtenez la disposition:

private void PerformLongRunningOperation()
    {
        using (BackgroundWorker worker = new BackgroundWorker())
        {
            worker.DoWork += delegate
                             {
                                 // perform long running operation here 
                             };
            worker.RunWorkerAsync();
        }
    }

EDIT:

Ok, j'ai mis en place un petit test pour voir un peu ce qui se passe avec l'élimination et autres joyeusetés:

using System;
using System.ComponentModel;
using System.Threading;

namespace BackgroundWorkerTest
{
    internal class Program
    {
        private static BackgroundWorker _privateWorker;

        private static void Main()
        {
            PrintThread("Main");
            _privateWorker = new BackgroundWorker();
            _privateWorker.DoWork += WorkerDoWork;
            _privateWorker.RunWorkerCompleted += WorkerRunWorkerCompleted;
            _privateWorker.Disposed += WorkerDisposed;
            _privateWorker.RunWorkerAsync();
            _privateWorker.Dispose();
            _privateWorker = null;

            using (var BW = new BackgroundWorker())
            {
                BW.DoWork += delegate
                                 {
                                     Thread.Sleep(2000);
                                     PrintThread("Using Worker Working");
                                 };
                BW.Disposed += delegate { PrintThread("Using Worker Disposed"); };
                BW.RunWorkerCompleted += delegate { PrintThread("Using Worker Completed"); };
                BW.RunWorkerAsync();
            }

            Console.ReadLine();
        }

        private static void WorkerDisposed(object sender, EventArgs e)
        {
            PrintThread("Private Worker Disposed");
        }

        private static void WorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            PrintThread("Private Worker Completed");
        }

        private static void WorkerDoWork(object sender, DoWorkEventArgs e)
        {
            Thread.Sleep(2000);
            PrintThread("Private Worker Working");
        }

        private static void PrintThread(string caller)
        {
            Console.WriteLine("{0} Thread: {1}", caller, Thread.CurrentThread.ManagedThreadId);
        }
    }
}

Voici le résultat:

Main Thread: 1
Private Worker Disposed Thread: 1
Using Worker Disposed Thread: 1
Private Worker Working Thread: 3
Using Worker Working Thread: 4
Using Worker Completed Thread: 4
Private Worker Completed Thread: 3

Depuis quelques tests, il semble que Dispose() a essentiellement aucun effet sur un initié BackgroundWorker. Si oui ou non vous appeler dans le champ d'application d'une instruction d'utilisation, ou de l'utilisation qu'il a déclaré dans le code et les déposer immédiatement et déréférencement d'elle, il continue à fonctionner normalement. Les Éliminés Événement se produit sur le thread principal, et le DoWork et RunWorkerCompleted se produire sur le pool de threads (celui qui est disponible lorsque l'événement est déclenché). J'ai essayé un cas où j'ai non la RunWorkerCompleted manifestation juste après que j'ai appelée dispose (donc avant DoWork eu la chance de terminer) et RunWorkerCompleted n'a pas d'incendie. Ce qui m'amène à croire que vous pouvez manipuler l'objet BackgroundWorker malgré qu'il soit éliminé.

Donc, comme d'autres l'ont mentionné, à ce moment, il semble bien que l'appel de Disposer n'est pas vraiment nécessaire. Cependant, je ne vois aucun mal à le faire, du moins, de mon expérience et de ces tests.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X