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」オブジェクトを含めました。この効果が見られるはずです (つまり、ターゲットはワーカーと共に停止します)。

スレッドが終了すると、re worker が収集されます。これが証明です。ワーカー レポートが終了すると、「worker finalized」が表示されます。

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 を呼び出す前にそれらを null にするか、別のコンテキストに移動します。

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 に答える