C# WinForm 使用 ThreadPool 与 CancellationToken 实现非阻塞多线程累加

Chloe
Chloe
发布时间:2026-10-14 | 5人浏览过
原创

在 Windows Forms (WinForm) 开发中,直接在主线程(UI线程)执行耗时计算会导致界面“假死”。本教程通过一个数字累加示例,展示如何使用 System.Threading.ThreadPool 执行后台运算,并结合 CancellationTokenSource 实现安全的取消机制,同时利用 this.Invoke 安全地跨线程更新 UI 控件。

核心难点:跨线程访问与协作取消

WinForm 的多线程编程主要解决两个问题:

  1. 跨线程 UI 访问:工作线程无法直接修改主线程创建的控件属性,否则抛出异常。
  2. 协作式取消:如何在运行中的长时间任务中优雅地停止计算,而不是强制杀掉线程。

第一步:设计支持取消的累计方法

将耗时的逻辑封装为独立方法,并接受 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 响应的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表