C# WinForm 使用 ThreadPool 与 CancellationToken 实现非阻塞多线程累加
在 Windows Forms (WinForm) 开发中,直接在主线程(UI线程)执行耗时计算会导致界面“假死”。本教程通过一个数字累加示例,展示如何使用 System.Threading.ThreadPool 执行后台运算,并结合 CancellationTokenSource 实现安全的取消机制,同时利用 this.Invoke 安全地跨线程更新 UI 控件。
核心难点:跨线程访问与协作取消
WinForm 的多线程编程主要解决两个问题:
- 跨线程 UI 访问:工作线程无法直接修改主线程创建的控件属性,否则抛出异常。
- 协作式取消:如何在运行中的长时间任务中优雅地停止计算,而不是强制杀掉线程。
第一步:设计支持取消的累计方法
将耗时的逻辑封装为独立方法,并接受 CancellationToken 参数以支持外部取消请求。该方法负责遍历数值、累加求和,并定时更新界面状态。
入口/动作:定义 CountTo 私有方法。
重要参数:countTo(目标数值),ct(取消令牌)。
/// <summary>
/// 从1累加到指定的值,为了让该方法支持取消操作所以需要CancellationToken参数
/// </summary>
private void CountTo(int countTo, CancellationToken ct)
{
int sum = 0;
for (; countTo > 0; countTo--)
{
// 1. 检查是否收到取消信号
if (ct.IsCancellationRequested)
{
break; // 优雅退出循环
}
// 2. 执行计算
sum += countTo;
// 3. 跨线程更新 UI
// Invoke 用于确保代码在创建 lbl_Status 的线程(主线程)上执行
this.Invoke(new Action(() => lbl_Status.Text = sum.ToString()));
// 模拟耗时操作或控制更新频率
Thread.Sleep(200);
}
}
第二步:启动线程池任务
为了节省资源并避免频繁创建销毁线程,建议使用 ThreadPool。点击“开始”按钮时,初始化取消源 CancellationTokenSource,并将任务投递到线程池。
入口/动作:btn_Count_Click 事件处理程序。
关键对象:CancellationTokenSource _cts(需声明为类成员变量)。
// 必须作为类的成员变量保存,以便后续调用 Cancel()
private CancellationTokenSource _cts;
private void btn_Count_Click(object sender, EventArgs e)
{
// 重置取消源
_cts = new CancellationTokenSource();
// 获取输入值
int target = int.Parse(txt_CountTo.Text);
// 将任务放入线程池执行
// state 参数在此处未使用,传 null 即可
ThreadPool.QueueUserWorkItem(state => CountTo(target, _cts.Token));
}
第三步:实现取消操作
当用户点击“取消”按钮时,只需调用 _cts.Cancel()。这会标记取消令牌为已取消状态,使得正在运行的 CountTo 方法在下一次循环检查时触发 break。
入口/动作:btn_Cancel_Click 事件处理程序。
private void btn_Cancel_Click(object sender, EventArgs e)
{
// 检查源是否存在,防止重复或错误操作
if (_cts != null)
{
_cts.Cancel();
}
}
完整代码结构参考
以下是整合后的 Form 类核心代码结构,包含了必要的命名空间引用及事件绑定:
using System;
using System.Threading;
using System.Windows.Forms;
namespace WinformApp
{
public partial class Form1 : Form
{
private CancellationTokenSource _cts;
public Form1()
{
InitializeComponent();
}
private void CountTo(int countTo, CancellationToken ct)
{
int sum = 0;
for (; countTo > 0; countTo--)
{
if (ct.IsCancellationRequested)
{
break;
}
sum += countTo;
// 跨线程安全更新 UI
this.Invoke(new Action(() => lbl_Status.Text = sum.ToString()));
Thread.Sleep(200);
}
}
private void btn_Count_Click(object sender, EventArgs e)
{
_cts = new CancellationTokenSource();
int target = int.Parse(txt_CountTo.Text);
ThreadPool.QueueUserWorkItem(state => CountTo(target, _cts.Token));
}
private void btn_Cancel_Click(object sender, EventArgs e)
{
if (_cts != null)
{
_cts.Cancel();
}
}
}
}
以上就是 C# WinForm 使用 ThreadPool 与 CancellationToken 实现非阻塞多线程操作的详细内容,更多关于 .NET 并发编程与 UI 响应的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









