メイン処理ジョブがフリーズしたかどうかを確認するために使用される「ウォッチドッグ」タイマーを含むプロダクション サービスに問題があります (これは、残念ながらテストで再現できない COM 相互運用の問題に関連しています)。
現在の仕組みは次のとおりです。
- 処理中、メイン スレッドは をリセットし
ManualResetEvent
、1 つのアイテムを処理し (これにはそれほど時間はかかりません)、イベントを設定します。その後、残りのアイテムの処理を続行します。 - 5 分ごとに、ウォッチドッグが
WaitOne(TimeSpan.FromMinutes(5))
このイベントを呼び出します。結果が false の場合、サービスは再起動されます。 - 通常の操作中に、処理に 5 分ほどかからない場合でも、このウォッチドッグによってサービスが再起動されることがあります。
複数のアイテムが処理を待っている場合Set()
、最初のアイテムが処理されてReset()
から 2 番目のアイテムが処理されるまでの時間が短すぎWaitOne()
て、イベントが設定されていることを認識していないように見えることが原因であると思われます。
私の理解でWaitOne()
は、ブロックされたスレッドはが呼び出されたときにシグナルを受信することが保証されていSet()
ますが、何か重要なものが欠けていると思います。
Thread.Sleep(0)
を呼び出した後に呼び出してコンテキストの切り替えを許可するとSet()
、WaitOne()
決して失敗しないことに注意してください。
以下に含まれているのは、私の製品コードと同じ動作を生成するサンプルです。が800 ミリ秒ごとに呼び出されているにもかかわらずWaitOne()
、5 秒間待機して失敗することがあります。Set()
private static ManualResetEvent _handle;
private static void Main(string[] args)
{
_handle = new ManualResetEvent(true);
((Action) PeriodicWait).BeginInvoke(null, null);
((Action) PeriodicSignal).BeginInvoke(null, null);
Console.ReadLine();
}
private static void PeriodicWait()
{
Stopwatch stopwatch = new Stopwatch();
while (true)
{
stopwatch.Restart();
bool result = _handle.WaitOne(5000, false);
stopwatch.Stop();
Console.WriteLine("After WaitOne: {0}. Waited for {1}ms", result ? "success" : "failure",
stopwatch.ElapsedMilliseconds);
SpinWait.SpinUntil(() => false, 1000);
}
}
private static void PeriodicSignal()
{
while (true)
{
_handle.Reset();
Console.WriteLine("After Reset");
SpinWait.SpinUntil(() => false, 800);
_handle.Set();
// Uncommenting either of the lines below prevents the problem
//Console.WriteLine("After Set");
//Thread.Sleep(0);
}
}
質問
Set()
密接に続いて呼び出すことReset()
は、ブロックされたすべてのスレッドが再開されることを保証しないことを理解していますが、待機中のスレッドが解放されることも保証されませんか?