Thread.Sleep背后的逻辑

在下面的WPF代码中,只有一个按钮和一个标签。 我希望这个行为是这样的:我会看到按钮被禁用,标签上写着“Doing Stuff”,然后会冻结那个UI。 但这种情况并非如此。 我甚至没有看到“Doing Stuff”标签UI被冻结到开头。 你能告诉我这背后的逻辑吗? 为什么Thread.Sleep会让UI立即冻结?

using System.Threading.Tasks; using System.Threading; using System.Windows; namespace MessagePump { ///  /// Interaction logic for MainWindow.xaml ///  public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void btnDoStuff_Click(object sender, RoutedEventArgs e) { btnDoStuff.IsEnabled = false; lblStatus.Content = "Doing Stuff"; Thread.Sleep(4000); lblStatus.Content = "Not doing anything"; btnDoStuff.IsEnabled = true; } } } ; 

Thread.Sleep阻塞当前线程 – 在本例中是UI线程 – 意味着不能完成其他任务。 因此,UI线程现在无法执行任何消息,如“刷新屏幕”,直到长时间阻止操作完成。

将有一个挂起的消息,如WM_PAINT告诉Windows必须重新绘制UI,但由于线程被Sleep操作阻止,因此无法执行该消息。 如果在Thread.Sleep之前添加以下代码行,您将看到在UI被冻结之前将刷新标签

 Application.DoEvents(); 

请注意,阻止UI线程并调用Application.DoEvents在大多数情况下并不是一个好的解决方案。 除了阻止UI线程和调用DoEvents之外,更有可能采用更好的方法来解决某个问题。 (例如,使用后台线程或任务来执行长时间运行的操作并使用事件来通知UI该任务的进度)。

永远不要在UI线程上执行长时间运行的任务,因为您的应用程序将无响应。 因此,最好将工作卸载到另一个线程。 从.NET 4开始,首选方法是使用TAP (基于任务的异步模式)。

我假设在你的问题中你使用Thread.Sleep来“模拟”你想要执行的一些cpu密集型算法。 你可以通过启动一个新的任务来完成这个:

 await Task.Run ( () => SomeLongRunningMethod()) 

Thread.Sleep阻止UI线程。 声明Click处理程序async并调用Task.Delay

 private async void btnDoStuff_Click(object sender, RoutedEventArgs e) { btnDoStuff.IsEnabled = false; lblStatus.Content = "Doing Stuff"; await Task.Delay(4000); lblStatus.Content = "Not doing anything"; btnDoStuff.IsEnabled = true; }