TNBLOG
首页
博客
视频
资源
问答
猿趣
手机
关于
搜索
收藏
便签
笔记
消息
创作
登录
剑轩
故如虹,知恩;故如月,知明
博主信息
排名
6
文章
6
粉丝
16
评论
8
文章类别
CSS
15篇
微服务
41篇
Git
14篇
.NET
102篇
移动开发
33篇
软件架构
23篇
.NET Core
119篇
.NET MVC
11篇
英语
3篇
随笔
86篇
Bootstrap
3篇
Redis
21篇
编辑器
10篇
Js相关
15篇
虚拟化
8篇
更多
Oracle
7篇
Python
14篇
数据库
26篇
EF
17篇
微信
3篇
前端
151篇
消息队列
6篇
docker
41篇
多线程
1篇
Java
4篇
软件基础
2篇
C++
2篇
WCF
7篇
Linux
7篇
nginx
5篇
K8S
9篇
ABP
2篇
最新文章
最新评价
{{item.articleTitle}}
{{item.blogName}}
:
{{item.content}}
关于我们
ICP备案 :
渝ICP备18016597号-1
网站信息:
2018-2024
TNBLOG.NET
技术交流:
群号656732739
联系我们:
contact@tnblog.net
欢迎加群
欢迎加群交流技术
原
注意async task返回 void 对象被释放问题
726
人阅读
2021/6/23 13:23
总访问:
3983814
评论:
0
收藏:
0
手机
分类:
.NET
`异步方法要尽量避免使用void返回值,就是不要写成async void这种写法`,就算不要返回值也要写成async task。因为一旦使用了async void这种写法,方法就不能被等待了,调用这个方法的地方就不会去等待这个方法的执行,就容易造成各种对象被释放的问题,常见的就是ef上下文对象在异步情况下被自动释放的问题。 C#的异步方法可以定义三种返回方式: `void` 、 `Task` 和 `Task<TResult>` 。其中, `Task<TResult>` 表示返回一个泛型任务,它可以在完成后提供一个T类型的结果值; Task 表示返回一个非泛型任务,它只表示一个异步操作的状态; void 表示不返回任何任务,也就是说,这个异步方法不能被调用者用类似"await"的关键字等待。 ### void类型的异步方法 ``` /// <summary> /// void类型的异步方法 /// </summary> public async void AddInquiryAndLoadPortAsync() { await DoAddInquiry(); } ``` 返回void的async方法不能被等待。这种方法只适用于异步事件处理器或其他不需要等待或错误处理的场景。返回void的async方法不能被等待,是因为它不返回任何对象,也就是说,它没有任何可被等待的任务。在C#中,要想一个对象能被等待,它必须有一个名为GetAwaiter的实例方法或扩展方法,这个方法可以返回一个awaiter对象,用于表示异步操作的状态和结果。而返回void的async方法不会产生任何对象,所以它没有GetAwaiter方法,也没有awaiter对象。因此,如果我们试图使用await关键字来等待一个返回void的async方法,编译器就会报错。这种方式又叫"发起并遗忘(Fire-and-forget)",只管发起调用,不管后续的执行状态和结果。 `异步方法要尽量避免使用void返回值,调用这个方法的地方就不会去等待这个方法的执行,就容易造成各种对象被释放的问题,常见的就是ef上下文对象在异步情况下被自动释放的问题。` ### 返回Task的异步方法 ``` /// <summary> /// 返回Task的异步方法 /// </summary> /// <returns></returns> public async Task AddInquiryAndLoadPortAsync() { await DoAddInquiry(); } ``` 返回`Task`或`Task<TResult>`的async方法可以被等待。它适用于大多数异步编程的场景,可以让调用方跟踪任务的执行状态。 这种方式也叫"等待并继续(Wait-and-continue)",发起调用后还继续跟踪任务的执行状态和结果。 ### 返回Task TResult的异步方法 ``` /// <summary> /// 返回Task<TResult>的异步方法 /// </summary> /// <returns></returns> public async Task<bool> AddInquiryAndLoadPortAsync() { bool isAdd = await DoAddInquiry(); return isAdd; } // 随便模拟一个异步方法 public Task<bool> DoAddInquiry() { return Task.Run<bool>(() => { return true; }); } ``` 和上面的方法原理一样,方法可以被等待,还可以在调用方通过`Task<TResult>`获得执行结果。能获取到方法的状态也能获取到方法的结果。 ## 相关原理解释 异步方法会被编译为一个状态机,状态机根据 await 将异步方法划分为若干片段,每执行完一个片段,就切出状态机,等待调度器重新调度。await 的原理就是调用 GetAwaiter() 方法获取 Awaiter 对象,状态机从而决定下一步动作。而 Awaiter 对象是从 Task 对象中获取的。如果异步方法的返回值是 void,也就意味着外部调用者无法获取 Awaiter 对象,其最严重的后果是,外部调用者无法捕获异步任务的异常。那么一旦异步任务发生异常,除非自行处理掉,否则该异常会直接沿着后台工作线程的调用堆栈直接传播到当前应用程序域 AppDomain,从而引发进程异常,导致整个进程崩溃。`所以,在实践中,异步方法尽量少用 void。不是说彻底禁止返回值为 void 的异步方法,而是要明白其工作原理。返回值为 void 的异步方法的最常见使用场景是异步事件`。但需要注意的是,由于外部调用者捕获不到异步事件里的异常,所以异步事件本身必须要通过 try-catch 处理掉自身的异常。
欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739
👈{{preArticle.title}}
👉{{nextArticle.title}}
评价
{{titleitem}}
{{titleitem}}
{{item.content}}
{{titleitem}}
{{titleitem}}
{{item.content}}