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

异步方法在C#中的使用

async/await:简化异步代码编写,await 暂停方法但不阻塞线程。

CancellationToken:用于取消异步任务,避免不必要的执行。
Task 生命周期:包括启动、运行、完成、取消、异常等状态。
WinForm UI 线程:异步任务需通过 Invoke 或 SynchronizationContext 与 UI 线程交互。

private async Task ShowPopupWithCancellationAsync(CancellationToken token)
{using (Form popupForm = new Form { Text = "可取消弹窗", Size = new Size(300, 200) }){Button cancelButton = new Button { Text = "取消", Dock = DockStyle.Bottom };using (CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(token)){cancelButton.Click += (s, e) => cts.Cancel();popupForm.Controls.Add(cancelButton);popupForm.Show();try{// 模拟长时间运行的任务for (int i = 0; i < 10; i++){await Task.Delay(1000, cts.Token); // 检查取消popupForm.Invoke((Action)(() => popupForm.Text = $"计数: {i + 1}"));}}catch (OperationCanceledException){Console.WriteLine("任务被取消");}finally{popupForm.Invoke((Action)(() => popupForm.Close()));}}}
}// 调用示例
private async void Button_Click(object sender, EventArgs e)
{using (CancellationTokenSource cts = new CancellationTokenSource()){// 可选:设置超时cts.CancelAfter(5000); // 5秒后自动取消await ShowPopupWithCancellationAsync(cts.Token);}
}
运行结果:
可能的取消场景
场景 A:用户点击 "取消" 按钮
触发cts.Cancel(),取消令牌被激活
Task.Delay检测到取消,抛出OperationCanceledException
进入catch块,输出 "任务被取消"
执行finally块,关闭弹窗
释放资源,流程结束场景 B:5 秒超时自动取消
当计数到 5 时(5 秒后),初始设置的cts.CancelAfter(5000)生效
取消令牌被激活,后续流程同场景 A
弹窗关闭,输出 "任务被取消"

CancellationTokenSource内部我感觉应该是用的单例和一个字典实现的。

Form、Button、TextBox、Label.Invoke()等等,都是通过 Invoke 方法在 UI 线程更新,不会抛出异常。

private async Task ShowPopupWithErrorHandlingAsync()
{try{Form popupForm = new Form { Text = "弹窗", Size = new Size(300, 200) };popupForm.Show();await Task.Run(() =>{// 模拟异常throw new InvalidOperationException("模拟错误");});}catch (Exception ex){// 在 UI 线程显示错误this.Invoke((Action)(() =>{MessageBox.Show($"错误: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);}));}
}

不可以直接调用 MessageBox.Show,否则会大概率抛出跨线程操作异常,异常是在 Task.Run 内部抛出的 ——Task.Run 会将任务调度到 ThreadPool 线程(非 UI 线程) 执行,因此 catch 块的执行上下文默认是这个非 UI 线程。如果直接在非 UI 线程调用MessageBox.Show,相当于在非 UI 线程创建 / 操作 UI 控件,违反了 WinForms 的 “UI 控件必须由创建它的线程访问” 的规则,必然触发跨线程异常。

使用 SynchronizationContext(捕获 UI 线程的同步上下文,在非 UI 线程中 Post 操作):
csharp


// 在 UI 线程(如构造函数、按钮点击事件)中先捕获上下文
private SynchronizationContext _uiSyncContext;
public Form1()
{InitializeComponent();_uiSyncContext = SynchronizationContext.Current; // 捕获 UI 线程的同步上下文
}// 在 catch 块中使用
catch (Exception ex)
{_uiSyncContext.Post(_ =>{MessageBox.Show($"错误: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);}, null);
}

http://www.dtcms.com/a/394420.html

相关文章:

  • js时间戳转换日期格式 yyyy-mm-dd
  • 信号处理方法概述
  • 固定收益理论(五)债券投资归因模型
  • 【论文速递】2025年第18周(Apr-27-May-03)(Robotics/Embodied AI/LLM)
  • 3D视觉——求出目标物体在相机坐标系下的位姿信息
  • 固态和机械硬盘损坏后的不同
  • Linux 基础IO
  • pandawiki ai 无法生成摘要
  • m语言可视化log中的变量信息
  • MySQL:库操作和常用数据类型
  • uniapp实现view块级元素横竖屏切换
  • 【编号74】河北地理基础数据(道路、水系、四级行政边界、地级城市、DEM等)
  • Python: 将wxauto发布为接口,并部署为Windows服务
  • 2025年度SEO优化公司
  • 基于Markdown的静态网站生成器完全指南
  • hot100——第十一周
  • 嵌入式(2)——HAL_GetTick()
  • 《第18课——C语言结构体:从Java的“豪华别墅“到C的“集装箱宿舍“——内存对齐、位域抠门与指针破门的底层狂欢》
  • 旅游线路预约小程序怎么搭建?景区售票团购小程序怎么做?
  • Redis未来发展趋势:技术演进与生态展望
  • 怎么重新映射windows终端的按键的功能
  • 【秋招笔试】2025.09.20哔哩哔哩秋招笔试真题
  • string 容器
  • MySQL零基础学习Day1——安装与配置
  • mysql重启,服务器计划重启,如何优雅地停止MySQL?
  • 源码加密知识产权(二) JS压缩和加密——东方仙盟元婴期
  • ​​[硬件电路-308]:双通道通用比较器TC75W57FK 功能概述与管脚定义
  • 华为MindIE 推理引擎:架构解析
  • 使用 modelscope gpu 跑通第一个 cuda 入门实例
  • Agent实战02-agent入门案例LlamaIndex