当前位置: 首页 > news >正文

Task异步练习(进度条)

WinForm窗体设计:

代码:

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace 进度条 { public partial class Form1 : Form { private CancellationTokenSource _cts;//通行证 private bool _isPause=false;//暂停标记 private bool _isRunning=false;//运动标记 private int _currentNum = 0;//值 public Form1() { InitializeComponent(); progressBar1.Minimum = 0;//设置最小进度条 progressBar1.Maximum = 100;//设置最大进度条 progressBar1.Value = 0;//进度初始值 dataGridView1.Columns.Add("colNum", "数字");//在表格中添加列(列名,显示在表格中列名) dataGridView1.Columns.Add("colVal", "值"); } private async Task RunLoopAsync(CancellationToken token)//从开启按钮传递过来的通行证,用来监听取消信号 { await Task.Run(async () => { for (; _currentNum <= 100; _currentNum++) { token.ThrowIfCancellationRequested(); // 暂停阻塞逻辑 while (_isPause) { await Task.Delay(1000, token); } int val = _currentNum * 10; // 后台子线程必须Invoke切UI线程更新控件 this.Invoke(new Action(() => { progressBar1.Value = _currentNum; label1.Text = $"当前进度:{_currentNum}/100,数字值:{val}"; dataGridView1.Rows.Add(_currentNum, val); })); await Task.Delay(500, token); } // for循环执行完毕,全部数字跑完 this.Invoke(new Action(() => { label1.Text = "全部执行完毕!"; MessageBox.Show("循环数字已到达100,任务自动结束!"); })); _currentNum = 0; this.Invoke(new Action(() => { progressBar1.Value = 0; })); }, token); // Task.Run 闭合 + 传入取消令牌 } private void btnPause_Click(object sender, EventArgs e) { if (!_isRunning) return; _isPause = true; label1.Text += "【已暂停】"; } private void btnContinue_Click(object sender, EventArgs e) { if(!_isRunning||!_isPause) return; _isPause=false; } private void btnCancel_Click(object sender, EventArgs e) { if(!_isRunning) return; _cts?.Cancel(); label1.Text = null; dataGridView1.Rows.Clear(); progressBar1.Value = 0; _currentNum = 0; } private async void btnStart_Click(object sender, EventArgs e) { if (_isRunning) { MessageBox.Show("任务正在运行,请勿重复开启!"); return; } _isRunning = true; _isPause = false; _cts = new CancellationTokenSource(); CancellationToken token = _cts.Token; try { await RunLoopAsync(token); } catch (OperationCanceledException) { label1.Text = "任务已取消"; } catch (Exception ex) { MessageBox.Show($"运行异常:{ex.Message}"); } finally { _isRunning = false; _cts?.Dispose(); _cts = null; } } } }

WinForm 异步后台进度条【暂停、继续、取消、断点续跑】终极知识总结

一、整体功能(你当前代码实现)

  1. 点击开启:后台线程循环 0~100,每 500ms 刷新进度条、Label、DataGridView
  2. 支持暂停 / 继续(软暂停,不销毁任务、可续跑)
  3. 支持取消(硬终止任务、清空界面)
  4. 防止重复开启多任务
  5. 断点续跑:暂停 / 取消后再次开启从上次数字继续
  6. 自带异常捕获、资源释放、线程安全、UI 不卡顿

二、全局变量 4 个(必考)

csharp

运行

private CancellationTokenSource _cts; // 任务取消器(发停止信号) private bool _isPause = false; // 暂停标记 private bool _isRunning = false; // 任务运行锁(防重复启动) private int _currentNum = 0; // 全局记录当前循环数字(实现断点续跑)

作用一句话

  1. _cts:负责取消任务
  2. _isPause:负责暂停 / 继续
  3. _isRunning:负责防止多开
  4. _currentNum:负责断点续跑

三、窗体构造函数知识点

csharp

运行

progressBar1.Minimum = 0; progressBar1.Maximum = 100; progressBar1.Value = 0; dataGridView1.Columns.Add("colNum", "数字"); dataGridView1.Columns.Add("colVal", "值");
  1. 进度条范围0~100和循环完全对齐
  2. 代码动态创建表格两列,无需手动设计

四、核心重点:为什么你的代码是真正后台线程

关键区别(你之前最大的疑惑)

1.async / await不会开后台线程

2.Task.Run才会开后台线程

你现在正确代码:

csharp

运行

await Task.Run(async () => { // 这里 100% 后台子线程!! });

所以:

  • 循环、计算、Delay全部跑在线程池后台
  • 不卡 UI
  • 必须 Invoke 更新控件(否则报错跨线程)

五、核心方法 RunLoopAsync 逐知识点

1. 方法标准规范

csharp

运行

private async Task RunLoopAsync(CancellationToken token)
  • 自定义异步方法必须返回Task不能 async void
  • token:接收取消信号,用来随时终止循环

2. 断点续跑 for 循环(高频考点)

csharp

运行

for (; _currentNum <= 100; _currentNum++)
  • 没有初始化语句
  • 循环变量用全局字段
  • 暂停、取消后数字不会丢 →实现断点续跑

3. 任务取消检测

csharp

运行

token.ThrowIfCancellationRequested();
  • 检测是否点击了取消
  • 点击取消直接抛异常 → 终止后台任务

4. 软暂停逻辑(重点)

csharp

运行

while (_isPause) { await Task.Delay(1000, token); }
  • 不销毁任务、不退出循环
  • 原地无限等待 →暂停
  • _isPause=false立刻继续

5. Invoke 跨线程更新 UI(必考)

csharp

运行

this.Invoke(new Action(() => { progressBar1.Value = _currentNum; label1.Text = $"当前进度:{_currentNum}/100,数字值:{val}"; dataGridView1.Rows.Add(_currentNum, val); }));

为什么必须 Invoke?

  • 循环代码在后台子线程
  • WinForm 控件只能由 UI 主线程访问
  • 子线程改控件 = 报错
  • Invoke:把子线程代码切回 UI 主线程执行

6. 异步延时

csharp

运行

await Task.Delay(500, token);
  • 不阻塞线程、不卡界面
  • 带 token:取消时立刻中断等待

六、任务跑完收尾逻辑

csharp

运行

this.Invoke(new Action(() => { label1.Text = "全部执行完毕!"; MessageBox.Show("循环数字已到达100,任务自动结束!"); })); _currentNum = 0;
  • 跑完归零,下次从头开始

七、四个按钮逻辑总结(超级清晰)

1. 开启按钮(核心入口)

  • 判断_isRunning防止重复开启
  • 创建_cts取消源
  • 传递 token 给后台任务
  • try-catch捕获取消异常、程序异常
  • finally 必执行:释放资源、解锁任务状态

2. 暂停按钮

csharp

运行

_isPause = true;

后台while进入死循环等待 → 暂停

3. 继续按钮

csharp

运行

_isPause = false;

退出 while 循环 → 继续跑

4. 取消按钮

csharp

运行

_cts.Cancel();
  • 给后台发终止信号
  • 清空表格、进度条、数字

八、三大核心机制区别(考试必考)

1. 后台线程机制

Task.Run→ 真正后台运行,UI 不卡、必须 Invoke

2. 暂停机制(软暂停)

布尔变量控制,任务不销毁、线程不退出、可续跑

3. 取消机制(硬终止)

CancellationTokenSource→ 强制终止任务,抛取消异常


九、异常处理机制

  1. OperationCanceledException用户手动取消任务 →正常预期操作
  2. 通用 Exception 程序 Bug、计算错误、控件错误 →兜底保护程序不崩溃

十、资源释放(重要)

csharp

运行

finally { _isRunning = false; _cts?.Dispose(); _cts = null; }
  • 无论成功、报错、取消一定执行
  • 释放取消令牌资源,防止内存泄漏

十一、你代码的完美架构总结(背诵版)

  1. Task.Run开启后台线程 → UI 永不卡死
  2. 全局变量记录状态→ 实现暂停、继续、续跑、防多开
  3. token 令牌→ 安全取消任务
  4. Invoke→ 解决跨线程 UI 报错
  5. async/await→ 优雅异步,不阻塞
  6. try-catch-finally→ 安全稳定、资源不泄漏

十二、最终一句话超级总结

  • Task.Run 负责后台运行
  • while (_isPause) 负责暂停
  • CancellationToken 负责取消
  • 全局变量负责续跑、防多开
  • Invoke 负责跨线程更新 UI
  • async/await 负责不卡界面
http://www.jsqmd.com/news/1144885/

相关文章:

  • BUUCTF findit
  • 用 conda-pack 打包带走,连网都不用
  • 本地知识库搭好了但不好用?RAG 调优实战:检索准确率从 40% 拉到 90%
  • BUUCTF luck_guy
  • WE Learn网课助手:5分钟掌握终极学习效率工具
  • 新功能解读系列文章(4):SSL 加密连接——数据校验传输安全再升级
  • 松下 A5/A6 伺服驱动器 X4 接口接线实战:位置模式 8 个关键引脚与 PLC 连接详解
  • 怎样高效使用Linux桌面便签工具Sticky:实用技巧完整教程
  • PayPal Ventures关停:60亿基金因战略调整落幕,被投企业何去何从?
  • Claude 有了“内心独白”?Anthropic 的 J-space 研究到底说明了什么
  • 【单片机毕业设计】基于 STM32/51 单片机的 MPU6050 倾角检测与倾斜报警装置设计,基于单片机的姿态角度采集与 LCD1602 显示报警系统设计(021201)
  • 什么是 IndexedDB?
  • Ragas 自动化评估代码示例
  • nVisual:基于历史趋势自动预测,扩容规划数据支撑率达90%
  • Translumo实战指南:Windows平台实时屏幕翻译高效方案
  • 为什么很多降重越改越高?如何选能同步降低查重率与 AIGC 标记的 AI 工具?
  • 【没想到我国的科技开发已经发展到这个地步】Super-LIO 项目分析与 Mid-360 无人机移植指南
  • BSON(二进制 JSON)
  • 【8】补充与拓展:Langchain的联网搜索Agent,使用Tavily工具
  • 穿透物理边界,重构时空秩序:基于纯视觉的营房全景透视与无感智控
  • Java集合扩容机制解析:ArrayList与HashMap底层原理及性能差异
  • 2026降AIGC革命:AI率92%暴降至5%!实测10款AI智能降重工具!10款工具深度解析!
  • 联合动力泰国工厂批量交付电驱系统,商用车业务占比约两成
  • 程序员就业:接口、数据和评估怎么串
  • Sentinel 流量治理实战:熔断降级限流 + Nacos 持久化
  • PLC控制气缸与电磁阀:从基础原理到多设备协同实战
  • 卡美德生物科普:CD244(2B4)在干细胞培养中的双向调控机制与应用
  • Orion与Shilpa Medicare深化合作,共同开发并供应欧洲市场纳武利尤单抗生物类似药
  • Dromara Sureness:把 REST API 鉴权这事做薄做快
  • Java随笔-GC收集器工作机制