スレッドはルート オブジェクトとしてカウントされます。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());
}
}