这个问题在这里已有答案:
在下面的WPF代码中,只有一个按钮和一个标签。我希望行为是这样的:我会看到按钮被禁用,标签上写着“Doing Stuff”,然后会冻结那个用户界面。但这种情况并非如此。我甚至没有看到“Doing Stuff”标签UI被冻结到开头。你能告诉我这背后的逻辑吗?为什么Thread.Sleep会让UI立即冻结?
using System.Threading.Tasks;
using System.Threading;
using System.Windows;
namespace MessagePump
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
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;
}