3

考虑这个代码块

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

一切都很好,但我想了解为什么对象(工作者、视图和更新)没有被垃圾收集

4

3 回答 3

7

线程算作根对象;我不确切地知道 BackgroundWorker 是如何运作的,但主线程方法似乎很可能会访问工作实例上的状态;因此,工作线程本身将使 BackgroundWorker 实例保持活动状态,直到(至少)线程退出。

当然; 集合还要求所有其他(活动)对象都已取消引用工作对象;另请注意,堆栈变量的集合在调试/发布中可能不同,并且附加/不附加调试器。

[编辑] 如前所述;工作人员(在您的代码中)上的事件处理程序将使“视图”和“更新”对象保持活动状态(通过委托),但不是相反。只要工人的寿命比“视图”和“更新”短,您就不必对取消订阅事件感到偏执。我已经编辑了代码以包含一个“SomeTarget”对象,该对象仅由工作人员引用:您应该看到这种效果(即目标与工作人员一起死亡)。

当线程死亡时,重新工作人员被收集:这是证明;在工作人员报告退出后,您应该会看到“工作人员已完成”:

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());
    }
}
于 2008-10-28T14:47:38.440 回答
0

事件处理程序是引用,因此在您将事件处理程序附加到工作人员之前,它不会被视为“无法访问”。

在 ComplitionCallback 中注意解开事件处理程序。

于 2008-10-28T14:56:50.903 回答
-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 的注意事项:

尝试运行以下程序,您会惊讶于 x 永远存在。

使用系统;

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();
    }
}
于 2008-10-28T23:13:55.517 回答