您是通过消息循环重新进入的受害者。您正在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 中只有一个线程泵送消息,排队的每个项目都按顺序处理。但是,当放入队列的方法本身可以泵送消息时,情况就不是这样了。在示例中,第 3 次操作实际上在第 2 次之前完成。该DoComplicatedWork
方法类似于SendWait
.
我应该回答你关于如何防止这种情况的第二个问题。Alock
无济于事,因为它们是可重入的(即同一个线程可以多次获取锁)。最好的方法是在方法内部禁用计时器或分离滴答处理程序,并在返回之前再次重新启用/附加处理程序。您也可以尝试一个简单的boolean
标志来指示您是否已经在该方法中,如果是则返回。