プログラムの存続期間中ずっと実行されるTimer
またはを使用する場合、ガベージ コレクションされないように、それらへの参照を保持する必要がありますか?Thread
以下のプログラムがクラス内に静的変数を持つ可能性があるという事実は脇に置いておいてください。timer
これは問題を示すための単なる例です。
public class Program
{
static void Main(string[] args)
{
CreateTimer();
Console.ReadLine();
}
private static void CreateTimer()
{
var program = new Program();
var timer = new System.Timers.Timer();
timer.Elapsed += program.TimerElapsed;
timer.Interval = 30000;
timer.AutoReset = false;
timer.Enabled = true;
}
private void TimerElapsed(object sender, ElapsedEventArgs e)
{
var timerCast = (Timer)sender;
Console.WriteLine("Timer fired at in thread {0}", GetCurrentThreadId());
timerCast.Enabled = true;
}
~Program()
{
Console.WriteLine("Program Finalized");
}
[DllImport("kernel32.dll")]
static extern uint GetCurrentThreadId();
}
上記の例ではタイマーを収集できますか? しばらく実行してみましたが、例外も呼び出されたことを示すメッセージも表示されませんでした~Program()
。
アップデート:私はから知りましたこの質問(ありがとう、セスクラン) スレッドは CLR によって追跡されることはわかっていますが、それでもタイマーについての回答が欲しいです。
ベストアンサー1
これは、System.Threading.Timerクラスで、他の場所に参照を保存していない場合のみ問題となります。このクラスには、いくつかのコンストラクタオーバーロードがあり、州オブジェクトは重要です。CLR はその状態オブジェクトに注意を払います。どこかで参照されている限り、CLR はタイマーをタイマー キューに保持し、タイマー オブジェクトはガベージ コレクションされません。ほとんどのプログラマーはその状態オブジェクトを使用しません。MSDN の記事ではその役割について説明されていません。
System.Timers.Timer は System.Threading.Timer クラスのラッパーであり、使いやすくなっています。特に、タイマーが有効になっている限り、状態オブジェクトを使用し、そのオブジェクトへの参照を保持します。
この場合、タイマーのEnabledプロパティは、AutoReset = falseであるため、Elapsedイベントハンドラに入るとfalseになります。したがって、タイマーはイベントハンドラに入るとすぐに収集の対象になります。ただし、送信者引数は、Enabled を true に戻すために必要です。これにより、ジッターが参照を報告するので、問題は発生しません。
Elapsedイベントハンドラには注意してください。そのメソッド内でスローされた例外は診断なしで取り込まれます。つまり、Enabledをtrueに戻さないということです。しなければならない適切な処理を実行するには、try/catch を使用します。意図的にプログラムを終了しない場合は、少なくともメイン プログラムに何かが動作していないことを知らせる必要があります。finally 句に Enabled = true を指定すると、タイマーのガベージ コレクションを回避できますが、プログラムが例外を何度もスローするリスクがあります。