技术归档文章随笔一句话导航搜索关于

UI线程阻塞问题以及解决方案

日期: 2025-03-12 分组: .NET 标签: .NETWinForm 3分钟 519字

原因

WinForm 采用单线程模型,所有 UI 控件必须在主线程(也叫 UI 线程)上更新。如果后台操作(如数据采集、处理)耗时较长,可能会导致界面卡顿或无响应。

解决方案

使用 BackgroundWorker

BackgroundWorker 是 WinForm 提供的一个用于处理后台操作的类。它可以在后台线程上运行代码,同时在 UI 线程上更新界面。

  • 新建按钮改名为 asyncButton
  • 新建 label 改名为 resultLabel
1
private BackgroundWorker backgroundWorker;
2
3
public MainForm()
4
{
5
InitializeComponent();
6
7
backgroundWorker = new BackgroundWorker();
8
backgroundWorker.DoWork += BackgroundWorker_DoWork;
9
backgroundWorker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
10
}
11
12
private void asyncButton_Click(object sender, EventArgs e)
13
{
14
if (!backgroundWorker.IsBusy)
15
{
23 collapsed lines
16
backgroundWorker.RunWorkerAsync();
17
}
18
}
19
20
private void BackgroundWorker_DoWork(object? sender, DoWorkEventArgs e)
21
{
22
Thread.Sleep(2000);
23
// 将结果存储在e.Result中
24
e.Result = "耗时两秒的任务处理完成,中间界面不出现卡死。";
25
}
26
27
private void BackgroundWorker_RunWorkerCompleted(object? sender, RunWorkerCompletedEventArgs e)
28
{
29
if (e.Error != null)
30
{
31
MessageBox.Show("Error: " + e.Error.Message);
32
}
33
else
34
{
35
if (e.Result is string)
36
resultLabel.Text = e.Result as string;
37
}
38
}

使用 Taskasync/await

  • 新建按钮改名为 taskButton
1
private async void taskButton_Click(object sender, EventArgs e)
2
{
3
resultLabel.Text = "耗时任务开始!";
4
5
var result = await Task.Run<string>(() =>
6
{
7
Thread.Sleep(2000);
8
return "耗时两秒的任务处理完成,中间界面不出现卡死。";
9
});
10
11
resultLabel.Text = result;
12
}

使用 Thread

直接使用 Thread 类来创建后台线程,需要注意线程间的通信问题,尤其是 UI 线程的更新需要使用 Invoke 方法。

  • 新建按钮改名为 threadButton
1
private void threadButton_Click(object sender, EventArgs e)
2
{
3
resultLabel.Text = "耗时任务开始!";
4
5
Thread thread = new Thread(() =>
6
{
7
Thread.Sleep(2000);
8
this.Invoke(new Action(() =>
9
{
10
resultLabel.Text = "耗时两秒的任务处理完成,中间界面不出现卡死。";
11
}));
12
});
13
thread.Start();
14
}

使用 Task 并结合 Progress<T>

  • 新建 ProgressBar 进度条控件改名为 progressBar
  • 新建按钮改名为 progressButton
1
private async void progressButton_Click(object sender, EventArgs e)
2
{
3
var progress = new Progress<int>(value =>
4
{
5
// 在UI线程上更新进度
6
progressBar.Value = value;
7
});
8
9
await Task.Run(() =>
10
{
11
for (int i = 0; i <= 100; i++)
12
{
13
// 这里不直接调用 progressBar 是因为Task内已经是在另一个线程上了
14
// progressBar 控件是在UI线程创建的,其他线程不能直接访问
15
// progressBar.Value = i;
5 collapsed lines
16
(progress as IProgress<int>).Report(i);
17
Thread.Sleep(50); // 模拟耗时操作
18
}
19
});
20
}
人应当是有理想的.
文章目录