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

C# TAP异步编程(Task/async/await)总结

C#中有个很好用的东西,TAP异步编程(Task-based Asynchronous Pattern),是目前C#推荐的异步编程模型。它基于 System.Threading.Tasks.Task 和 async/await 关键字,旨在简化异步代码的编写、调试和维护。TAP 是现代 .NET 应用开发中最常用、最推荐的异步编程方式。
要实现TAP,有三个基本元素:Task、async和await。

  • Task: 表示一个异步操作,可能没有返回值(Task)或有返回值(Task<T>
  • async: 标记一个方法为异步
  • await: 用于等待一个异步操作完成,不会阻塞当前线程。

在Winform中,使用好TAP编程,可以方便的将异步回调操作变为同步,且不会阻塞UI线程! 非常优雅。

千问3给出的TAP优势:

特性说明
简化异步代码使用 async/await 避免了复杂的回调嵌套(回调地狱)。
线程友好不会阻塞主线程(如 UI 线程),提升用户体验。
异常处理统一异常可以通过 try/catch 捕获,不需要额外的错误回调。
取消支持支持通过 CancellationToken 安全取消异步操作。
上下文感知通过 SynchronizationContext 自动恢复执行上下文(如 UI 线程)

1.回调封装为异步

回调的关键是需要有key来识别是哪次调用!
通过TaskCompletionSource来封装回调执行时传输的结果数据。
通过await 一个Task实现阻塞等待。
基本代码如下:

public static class CallBackAsync{// 存储异步任务的上下文private static readonly ConcurrentDictionary<int, TaskCompletionSource<int>> _pendingTasks = new ConcurrentDictionary<int, TaskCompletionSource<int>>();// 回调中调用public static void OnCallBackAsync(int key, int result){if (_pendingTasks.TryRemove(key, out var tcs)){tcs.TrySetResult(result);//这里会激发await,并返回结果}}// 异步调用public static async Task<int> ChannelCtrlAsync(int key){var tcs = new TaskCompletionSource<int>();// 缓存任务上下文_pendingTasks[key] = tcs;// 调用原生同步方法int ret = NativeFunc(key);if (ret != 0){_pendingTasks.TryRemove(key, out _);tcs.TrySetException(new Exception($"{ret}")); //通过异常码返回错误!}return await tcs.Task; //关键点}// 原生方法public static int NativeFunc(int key){Console.WriteLine("NativeFunc");//异常情况//return -1;//线程中模拟回调Task.Run(() =>{OnCallBackAsync(1, 99);});return 0;}}

这里用ConcurrentDictionary字典封装了key和TaskCompletionSource的上下文信息,异步调用时要创建一份,回调时进行查找并删除。
注意当原生调用立即返回错误且不进回调时,产生的错误码没法通过TaskCompletionSource传出,此时可通过异常机制来输出。

按钮调用的实现:

private async void button1_Click(object sender, EventArgs e) //必须async标记{try{var result = await CallBackAsync.ChannelCtrlAsync(1).ConfigureAwait(true); // 恢复UI上下文//继续干其他事情,UI不会阻塞}catch (Exception ex){//表示底层调用直接返回了错误码Console.WriteLine($"底层直接返回,返回值:{ex.Message}");}}

2.阻塞调用封装

此时无需TaskCompletionSource去手动等待,比如串口的阻塞式异步:

public async Task SendDataAsync(string strText){return await Task.Run(() =>{m_Port.Write(strText);});}

3.任务取消

通过传入参数CancellationTokenSource 对象,可以在外部调用Cancel()方法取消任务。

public async Task SendXXXAsync(string strText, CancellationTokenSource cts){return await Task.Run(() =>{while(1){if (cts != null && cts.Token.IsCancellationRequested){return;}m_Port.Write(部分数据); //循环发送使用}});}

4.超时处理

var timeoutTask = Task.Delay(TimeSpan.FromSeconds(3));
var completedTask = await Task.WhenAny(task, timeoutTask);
if (completedTask == timeoutTask)
{// 超时处理逻辑tcs.TrySetException(new Exception("ERR_CTRL_TIMEOUT")); //通过异常码返回错误!
}
return await tcs.Task;
http://www.xdnf.cn/news/13963.html

相关文章:

  • VRFF: Video Registration and Fusion Framework
  • 机器学习与深度学习20-数学优化
  • 2025.06.12【3D曲线图】|用Python绘制DNA甲基化3D曲线图(以CpG位点为例)
  • 局域网内 100 台设备同屏直播技术方案
  • 【总天数两种算法相互印证正确】2022-4-13
  • flowable查询历史流程实例时条件变量的类型问题
  • 实战解析:如何用克魔(KeyMob)等工具构建iOS应用稳定性与数据可观测体系
  • 【web应用】若依框架:若依框架中的面包屑导航与顶部导航栏:设计与实现
  • 电感详解同时其主要特性参数是什么?都有涉及哪些方面?
  • Wireshark 的基本使用
  • vulnyx Exec writeup
  • C++内存管理与编译链接
  • 芯片制程变化
  • centos 7.9 升级ssh版本 7.4p1 升级到 8.2p1
  • Spring AI Chat Client API 指南
  • uni-app项目实战笔记2--使用swiper实现纵向轮播图
  • 常见数据结构
  • Java中的classpath
  • 1.ES介绍
  • 算法第14天|继续学习二叉树:找二叉树左下角的值、二叉树路径总和、从中序遍历与后序遍历序列构建二叉树
  • 解决 PyTorch 与 Python 3.12 的兼容性问题:`operator torchvision::nms does not exist` 深度解析
  • leetcode 路径总和III java
  • 【unitrix】1.2 unitrix 物理量计算库(lib.rs)
  • springboot集成minio详细流程代码
  • 报表工具顶尖对决系列—关联过滤
  • [原创]X86C++反汇编03.除法的优化
  • 使用Nginx 如何解决Access-Control-Allow-Origin问题
  • 【大模型-写作】LLMxMapReduce-V2 自动修改大纲 生成高质量文章
  • 在macOS上运行Linux容器的方法
  • go-carbon v2.6.8 发布,轻量级、语义化、对开发者友好的 golang 时间处理库