あなたはメッセージ ループ経由で再突入の被害者です。timer1_Tick
メッセージループを介して間接的に関数に再帰しています。何が起こっているかというと、メッセージが処理されたかどうかを監視するために、SendKeys.SendWait
別のメッセージループ内で (別のスレッドではなく) スピンアップされているということです。次に、別のスレッドで、この内部ループでメッセージが処理されている間に、タイマーが起動し、tick 関数を再度呼び出すメッセージをポストします。陽気さが続きます。
おそらく、単純化された例が役立つでしょう。これを実行して、出力を観察します。
public class Program
{
private static Queue<Action> queue = new Queue<Action>();
public static void Main(string[] args)
{
// put three things in the queue.
// In a simple world, they would finish in order.
queue.Enqueue(() => DoWork(1));
queue.Enqueue(() => DoComplicatedWork(2));
queue.Enqueue(() => DoWork(3));
PumpMessages();
}
private static void PumpMessages()
{
while (queue.Count > 0)
{
Action action = queue.Dequeue();
action();
}
}
private static void DoWork(int i)
{
Console.WriteLine("Doing work: {0}", i);
}
private static void DoComplicatedWork(int i)
{
Console.WriteLine("Before doing complicated work: {0}", i);
PumpMessages();
Console.WriteLine("After doing complicated work: {0}", i);
}
}`
キューに入れられた各アイテムが順番に処理される UI にはスレッド ポンプ メッセージが 1 つしかないため、一種の想定です。ただし、キューに入れられたメソッド自体がメッセージをポンピングできる場合、これは当てはまりません。この例では、3 番目の操作が実際には 2 番目の操作の前に完了しています。このDoComplicatedWork
方法は、 で行われていることと似ていSendWait
ます。
これを防ぐ方法についての 2 番目の質問に答える必要があります。lock
それらは再入可能であるため (つまり、同じスレッドが複数回ロックを取得できる)、Aは役に立ちません。最善の方法は、タイマーを無効にするか、メソッド内でティック ハンドラーをデタッチし、戻る前にハンドラーを再度有効化/アタッチすることです。また、単純なboolean
フラグを試して、既にメソッド内にいるかどうかを示し、そうである場合は返すこともできます。