2008-10-28 6 views
3

considèrent que ce bloc de codeQuand mon exemple BackgroundWorker être des ordures collectées

public void ManageInstalledComponentsUpdate() 
     { 
      IUpdateView view = new UpdaterForm(); 
      BackgroundWorker worker = new BackgroundWorker(); 
      Update update = new Update(); 
      worker.WorkerReportsProgress = true; 
      worker.WorkerSupportsCancellation = true; 
      worker.DoWork += new DoWorkEventHandler(update.DoUpdate); 
      worker.ProgressChanged += new ProgressChangedEventHandler(view.ProgressCallback); 
      worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(view.CompletionCallback);    
      worker.RunWorkerAsync(); 
      Application.Run(view as UpdaterForm);  
     } 

Tout fonctionne très bien, mais je veux comprendre pourquoi ne sont pas les déchets collectés les objets (travailleur, vue et mise à jour)

+0

Comment savez-vous qu'ils ne sont pas collectés? – StingyJack

Répondre

7

Les threads comptent comme des objets racine; Je ne sais pas exactement comment BackgroundWorker fonctionne, mais il semble probable que la méthode de thread primaire va accéder à l'état sur l'instance de travail; en tant que tel, le thread de travail gardera en vie l'instance de BackgroundWorker jusqu'à ce que (au moins) le thread se soit arrêté.

Bien sûr; la collection exige également que tous les autres objets (vivants) aient dé-référencé l'objet de travail; notez également que la collection de variables de pile peut être différente dans le débogage/la libération et avec/sans un débogueur attaché.

[edit] Comme cela a également été noté; les gestionnaires d'événements sur le worker (dans votre code) garderont les objets "view" et "update" en vie (via le délégué), mais pas l'inverse. Tant que le travailleur a une durée de vie plus courte que la «vue» et la «mise à jour», vous n'avez pas besoin d'être paranoïaque à propos de la désinscription des événements. J'ai modifié le code pour inclure un objet "SomeTarget" qui n'est référencé que par le worker: vous devriez voir cet effet (c'est-à-dire que la cible meurt avec le worker).

Re travailleur récupéré lorsque le fil meurt: voici la preuve; vous devriez voir « travailleur finalisé » après que le travailleur signale la sortie:

using System; 
using System.ComponentModel; 
using System.Threading; 
using System.Windows.Forms; 
class Demo : Form 
{ 
    class ChattyWorker : BackgroundWorker 
    { 
     ~ChattyWorker() 
     { 
      Console.WriteLine("Worker finalized"); 
     } 
    } 
    class SomeTarget 
    { 
     ~SomeTarget() 
     { 
      Console.WriteLine("Target finalized"); 
     } 
     public SomeTarget() 
     { 
      Console.WriteLine("Target created"); 
     } 
     public void Foo(object sender, EventArgs args) 
     { 
      Console.WriteLine("Foo"); 
     } 
    } 
    static void Collect(object sender, EventArgs args) 
    { 
     Console.WriteLine("Collecting..."); 
     GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); 
    } 
    protected override void OnLoad(EventArgs e) 
    { 
     base.OnLoad(e); 

     System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer(); 
     timer.Interval = 100; 
     timer.Tick += Collect; 
     timer.Start(); 

     ChattyWorker worker = new ChattyWorker(); 
     worker.RunWorkerCompleted += new SomeTarget().Foo; 
     worker.DoWork += delegate 
     { 
      Console.WriteLine("Worker starting"); 
      for (int i = 0; i < 10; i++) 
      { 
       Thread.Sleep(250); 
       Console.WriteLine(i); 
      } 
      Console.WriteLine("Worker exiting"); 
     }; 
     worker.RunWorkerAsync(); 
    } 
    [STAThread] 
    static void Main() 
    { // using a form to force a sync context 
     Application.Run(new Demo()); 
    } 
} 
+0

J'aimerais savoir pourquoi cela a été voté ... –

0

Les gestionnaires d'événements sont des références, donc jusqu'à ce que vous avez gestionnaire d'événement attaché au travailleur, il ne serait pas considéré comme « inaccessible ».

Dans ComplitionCallback, veillez à décrocher les gestionnaires d'événements.

+0

Les délégués sont des références à sens unique; les événements sur BackgroundWorker garderont les objets "view" et "update" en vie, mais pas l'inverse. c'est-à-dire que si le travailleur sort de la portée de toute façon, il n'y a pas énormément d'avantages à décrocher les manipulateurs. Sentez-vous libre, cependant. –

-1

Ces objets variables locaux sont conservés jusqu'à la fin de la fonction, c'est-à-dire lorsque le formulaire se termine. Donc les annuler avant l'appel à Exécuter, ou les déplacer dans un contexte différent.

public void ManageInstalledComponentsUpdate() { 
    UpdaterForm form = new UpdaterForm(); 
    FireAndForgetWorker(form); 
    Application.Run(form); //does not return until form exits 
} 

void FireAndForgetWorker(IUpdateView view) { 
    BackgroundWorker worker = new BackgroundWorker(); 
    Update update = new Update(); 
    worker.WorkerReportsProgress = true; 
    worker.WorkerSupportsCancellation = true; 
    worker.DoWork += new DoWorkEventHandler(update.DoUpdate); 
    worker.ProgressChanged += new ProgressChangedEventHandler(view.ProgressCallback); 
    worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(view.CompletionCallback); 
    worker.RunWorkerAsync(); 
} 

Une note à vsick:

Essayez d'exécuter le programme suivant, vous serez surpris que x vit toujours.

en utilisant System;

class FailsOnGarbageCollection 
{ ~FailsOnGarbageCollection() { throw new NotSupportedException(); } } 

class Program{ 
    static void WaitForever() { while (true) { var o = new object(); } } 

    static void Main(string[] args) 
    { 
     var x = new FailsOnGarbageCollection(); 
     //x = null; //use this line to release x and cause the above exception 
     WaitForever(); 
    } 
} 
+0

Ce n'est pas correct. Les variables locales ne sont pas conservées lorsqu'elles ne sont pas utilisées dans le reste de la fonction (y compris 'this'!). – svick

+0

Désolé vsick, mais c'est juste. Essayez le programme ci-dessus pour voir pourquoi vous devez annuler ces variables. – jyoung

+0

Essayez le programme en mode * Release * pour voir que ** vous n'avez pas à ** annuler les variables (au moins dans ce cas, il peut être approprié par exemple pour les champs). – svick

Questions connexes