2008-10-28 3 views
3

считают этот блочный кодКогда мой экземпляр BackgroundWorker быть мусора

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);  
     } 

Это все прекрасно работает, но я хочу, чтобы понять, почему объекты (работник, просмотр и обновление) не получают мусора

+0

Откуда вы знаете, что они не собираются? – StingyJack 2008-10-28 15:14:35

ответ

7

Количество потоков в виде корневых объектов; Я не знаю точно, как работает BackgroundWorker, но кажется вероятным, что метод первичного потока будет иметь доступ к состоянию на рабочем экземпляре; как таковой, сам рабочий поток будет поддерживать экземпляр BackgroundWorker до тех пор, пока (по крайней мере) поток не выйдет.

Конечно; коллекция также требует, чтобы все другие (живые) объекты отменили ссылку на рабочий объект; обратите внимание также, что коллекция переменных стека может отличаться в отладке/выпуске и с/без отладчика.

[редактировать] Как также было отмечено; обработчики событий на рабочем месте (в вашем коде) будут сохранять живые объекты «просмотра» и «обновления» (через делегата), но не наоборот. До тех пор, пока у работника будет более короткая жизнь, чем «вид» и «обновление», вам не нужно получать параноик об отмене подписки на события. Я редактировал код для включения объекта «SomeTarget», на который ссылается только работник: вы должны увидеть этот эффект (т. Е. Цель умирает вместе с рабочим).

Re рабочий сбор собран, когда нить умирает: вот доказательство; Вы должны увидеть «работник доработан» после того, как работник сообщает выхода:

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

Хотелось бы узнать, почему это получилось - проголосовали ... – 2008-10-28 15:20:22

0

обработчики событий ссылки, так что, пока у вас есть обработчик события к работнику, он не будет считаться «недостижим».

В вашем ComplitionCallback позаботьтесь о снятии обработчиков событий.

+0

Делегаты - односторонние ссылки; события на BackgroundWorker будут содержать объекты «просмотра» и «обновления», но не наоборот. т. е. если работник все равно выходит из сферы видимости, нет никакой огромной выгоды при отцеплении обработчиков. Не стесняйтесь. – 2008-10-28 15:19:14

-1

Эти локальные переменные объекты сохраняются до тех пор, пока функция не выйдет, то есть когда форма выходит. Поэтому обнулите их перед вызовом Run или переместите их в другой контекст.

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(); 
} 

Примечание к vsick:

Попробуйте запустить следующую программу, вы будете удивлены, что х живет вечно.

с использованием системы;

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

Это не так. Локальные переменные не сохраняются в живых, если они не используются в остальной части функции (включая `this`!). – svick 2010-10-07 10:43:10

Смежные вопросы