原因
WinForm采用单线程模型,所有UI控件必须在主线程(也叫UI线程)上更新。如果后台操作(如数据采集、处理)耗时较长,可能会导致界面卡顿或无响应。
解决方案
使用 BackgroundWorker
BackgroundWorker 是WinForm提供的一个用于处理后台操作的类。它可以在后台线程上运行代码,同时在UI线程上更新界面。
- 新建按钮改名为 asyncButton
- 新建label 改名为 resultLabel
1 | private BackgroundWorker backgroundWorker; |
使用 Task 和 async/await
- 新建按钮改名为 taskButton
1
2
3
4
5
6
7
8
9
10
11
12private async void taskButton_Click(object sender, EventArgs e)
{
resultLabel.Text = "耗时任务开始!";
var result = await Task.Run<string>(() =>
{
Thread.Sleep(2000);
return "耗时两秒的任务处理完成,中间界面不出现卡死。";
});
resultLabel.Text = result;
}
使用 Thread
直接使用Thread类来创建后台线程,需要注意线程间的通信问题,尤其是UI线程的更新需要使用Invoke方法。
- 新建按钮改名为 threadButton
1
2
3
4
5
6
7
8
9
10
11
12
13
14private void threadButton_Click(object sender, EventArgs e)
{
resultLabel.Text = "耗时任务开始!";
Thread thread = new Thread(() =>
{
Thread.Sleep(2000);
this.Invoke(new Action(() =>
{
resultLabel.Text = "耗时两秒的任务处理完成,中间界面不出现卡死。";
}));
});
thread.Start();
}
使用 Task 并结合 Progress<T>
- 新建 ProgressBar 进度条控件改名为 progressBar
- 新建按钮改名为 progressButton
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20private async void progressButton_Click(object sender, EventArgs e)
{
var progress = new Progress<int>(value =>
{
// 在UI线程上更新进度
progressBar.Value = value;
});
await Task.Run(() =>
{
for (int i = 0; i <= 100; i++)
{
// 这里不直接调用 progressBar 是因为Task内已经是在另一个线程上了
// progressBar 控件是在UI线程创建的,其他线程不能直接访问
// progressBar.Value = i;
(progress as IProgress<int>).Report(i);
Thread.Sleep(50); // 模拟耗时操作
}
});
}