您已经发现了为什么永远不应该使用 Thread.Sleep。它仅对两件事有用。(1) 编写需要模拟线程忙碌一定秒数的测试用例,以及 (2) 休眠 0 毫秒告诉操作系统“如果存在一个想要”;这是礼貌的事情。
由于您发现的原因,您永远不应该使用 thread.Sleep 来引入延迟。您正在设置一个属性,但设置一个属性不会导致操作系统重新绘制屏幕。考虑是否这样做;您可能在一个方法中有一千个属性集,并且您必须在所有属性集之后重新绘制屏幕,这看起来很难看并且非常慢。
取而代之的是设置了属性,并且对象向操作系统记录了该线程何时可用于再次处理操作系统消息,请重新绘制我。您的程序不是告诉操作系统“我完成了,继续看看是否有任何消息给我”,而是您希望线程在 9 秒内什么都不做。
现在,您可以通过调用 DoEvents 告诉程序检查消息,但使用 DoEvents 也是一个坏主意,您不应该这样做。这样做本质上会导致您的程序表现出注意力缺陷障碍的症状;你还没有完成当前的工作,你正在寻找是否有新的工作要做而不从调用堆栈中删除旧的工作!假设这些新工作依次被打断,以此类推。堆栈无限制地增长,这是非常糟糕的。DoEvents 是一种“最糟糕的做法”,就像休眠线程一样。你可以在简单的小程序中摆脱它,但是当程序变得复杂时它会导致大麻烦。
此外:是的,DoEvents 将绘制您的控件,但仅此而已。在接下来的 9 秒内,应用程序将在用户看来完全挂起。这是非常糟糕的用户体验。
如果要引入延迟,正确的做法是异步等待。在 C# 4 和更早的版本中,执行此操作的标准方法是创建一个计时器,当计时器计时,执行下一件事。
现在,您说您不能使用计时器,因为您需要从 UI 线程访问控件。没关系。计时器的滴答事件处理程序将在 UI 线程上运行,而不是在单独的线程上。您可以安全地使用计时器。
在 C# 5 中,正确的做法是使用 newawait
关键字来引入异步等待。也就是说,等待在等待时执行其他操作,而不是在等待时进入睡眠状态。在 C# 5 中,您可以将代码编写为:
UISystem.SetScene(Scene_Menu);
await Task.Delay (9000);
p.Text="HELLO";
await Task.Delay(9000);
p.Text="WORLD";
C# 5 目前处于测试阶段;有关此新功能的详细信息,请参阅:
http://msdn.microsoft.com/en-us/async
有关异步的简要介绍以及 DoEvents 为何是坏消息的解释,请参阅我的 MSDN 杂志文章:
http://msdn.microsoft.com/en-us/magazine/hh456401.aspx