C#使用异步操作时的注意要点(翻译)
异步操作时应注意的要点
- 使用异步方法返回值应避免使用void
- 对于预计算或者简单计算的函数建议使用Task.FromResult代替Task.Run
- 避免使用Task.Run()方法执行长时间堵塞线程的工作
- 避免使用Task.Result和Task.Wait()来堵塞线程
- 建议使用await来代替continueWith任务
- 创建TaskCompletionSource时建议使用TaskCreationOptions.RunContinuationsAsynchronously属性
- 建议使用CancellationTokenSource(s)进行超时管理时总是释放(dispose)
- 建议将协作式取消对象(CancellationToken)传递给所有使用到的API
- 建议取消那些不会自动取消的操作(CancellationTokenRegistry,timer)
- 使用StreamWriter(s)或Stream(s)时在Dispose之前建议先调用FlushAsync
- 建议使用 async/await而不是直接返回Task
使用场景
异步操作时需要注意的要点
1.使用异步方法返回值应当避免使用void
在使用异步方法中最好不要使用void当做返回值,无返回值也应使用Task作为返回值,因为使用void作为返回值具有以下缺点
- 无法得知异步函数的状态机在什么时候执行完毕
- 如果异步函数中出现异常,则会导致进程崩溃
❌异步函数不应该返回void
static void Main(string[] args)
{
try
{
// 如果Run方法无异常正常执行,那么程序无法得知其状态机什么时候执行完毕
Run();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.Read();
}
static async void Run()
{
// 由于方法返回的为void,所以在调用此方法时无法捕捉异常,使得进程崩溃
throw new Exception("异常了");
await Task.Run(() => { });
}
☑️应该将异步函数返回Task
static async Task Main(string[] args)
{
try
{
// 因为在此进行await,所以主程序知道什么时候状态机执行完成
await RunAsync();
Console.Read();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
static async Task RunAsync()
{
// 因为此异步方法返回的为Task,所以此异常可以被捕捉
throw new Exception("异常了");
await Task.Run(() => { });
}
注:事件是一个例外,异步事件也是返回void
2.对于预计算或者简单计算的函数建议使用Task.FromResult代替Task.Run
对于一些预先知道的结果或者只是一个简单的计算函数,使用Task,FromResult要比Task.Run性能要好,因为Task.FromResult只是创建了一个包装已计算任务的任务,而Task.Run会将一个工作项在线程池进行排队,计算,返回.并且使用Task.FromResult在具有SynchronizationContext 程序中(例如WinForm)调用Result或wait()并不会死锁(虽然并不建议这么干)
❌对于预计算或普通计算的函数不应该这么写
public async Task<int> RunAsync()
{
return await Task.Run(()=>1+1);
}
☑️而应该使用Task.FromResult代替
public async Task<int> RunAsync()
{
return await Task.FromResult(1 + 1);
}
还有另外一种代替方法,那就是使用ValueTask类型,ValueTask是一个可被等待异步结构,所以并不会在堆中分配内存和任务分配,从而性能更优化.
☑️使用ValueTask代替
static async Task Main(string[] args)
{
await AddAsync(1, 1);
}
static ValueTask<int> AddAsync(int a, int b)
{
// 返回一个可被等待的ValueTask类型
return new ValueTask<int>(a + b);
}
注: ValueTask结构是C#7.0加入的,存在于Sysntem,Threading.Task.Extensions包中
3.避免使用Task.Run()方法执行长时间堵塞线程的工作
长时间运行的工作是指在应用程序生命周期执行后台工作的线程,如:执行processing queue items,执行sleeping,执行waiting或者处理某些数据,此类线程不建议使用Task.Run方法执行,因为Task.Run方法是将任务在线程池内进行排队执行,如果线程池线程进行长时间堵塞,会导致线程池增长,进而浪费性能,所以如果想要运行长时间的工作建议直接创建一个新线程进行工作
❌下面这个例子就利用了线程池执行长时间的阻塞工作
public class QueueProcessor
{
private readonly BlockingCollection<Message> _messageQueue = new BlockingCollection<Message>();
public void StartProcessing()
{
Task.Run(ProcessQueue);
}
public void Enqueue(Message message)
{
_messageQueue.Add(message);
}
private void ProcessQueue()
{
foreach (var item in _messageQueue.GetConsumingEnumerable())
{
ProcessItem(item);
}
}
private void ProcessItem(Message message) { }
}
☑️所以应该改成这样
public class QueueProcessor
{
private readonly BlockingCollection<Message> _messageQueue = new BlockingCollection<Message>();
public void StartProcessing()
{
var thread = new Thread(ProcessQueue)
{
// 设置线程为背后线程,使得在主线程结束时此线程也会自动结束
IsBackground = true
};
thread.Start();
}
public void Enqueue(Message message)
{
_messageQueue.Add(message);
}
private void ProcessQueue()
{
foreach (var item in _messageQueue.GetConsumingEnumerable())
{
ProcessItem(item);
}
}
private void ProcessItem(Message message) { }
}
C#使用异步操作时的注意要点(翻译)的更多相关文章
- ajax请求为异步操作时,返回的数据不会被并列函数执行
ajax请求为异步操作时,返回的数据不会被并列函数执行
- - Permission 运行时权限 总结 翻译 MD
目录 目录 对运行时权限的一些理解 运行时权限使用案例 开源库:PermissionsDispatcher 注解 使用案例 使用步骤 测试代码 自动生成的类 官方文档:请求权限 Add permiss ...
- 26计算限制的异步操作01-CLR
由CLR via C#(第三版) ,摘抄记录... 异步优点:在GUI应用程序中保持UI可响应性,以及多个CPU缩短一个耗时计算所需的时间. 1.CLR线程池基础:为提高性能,CLR包含了代码来管理他 ...
- [翻译]扩展C#中的异步方法
翻译自一篇博文,原文:Extending the async methods in C# 异步系列 剖析C#中的异步方法 扩展C#中的异步方法 C#中异步方法的性能特点. 用一个用户场景来掌握它们 在 ...
- C#执行异步操作的几种方式比较和总结
C#执行异步操作的几种方式比较和总结 0x00 引言 之前写程序的时候在遇到一些比较花时间的操作例如HTTP请求时,总是会new一个Thread处理.对XxxxxAsync()之类的方法也没去了解过, ...
- 【C#进阶系列】26 计算限制的异步操作
什么是计算限制的异步操作,当线程在要使用CPU进行计算的时候,那么就叫计算限制. 而对应的IO限制就是线程交给IO设备(键鼠,网络,文件等). 第25章线程基础讲了用专用的线程进行计算限制的操作,但是 ...
- uart启示2_异步操作的bug
发现代码中的隐藏bug真的是一件令人振奋的事情,当然也会疲倦那么一下午! 这个bug只有在一种在一个2604计数周期的一种情况下发生,所以即使是大量的仿真,未必也会发现的了,只有在以后的设计过程中,遇 ...
- 转载:ZooKeeper Programmer's Guide(中文翻译)
本文是为想要创建使用ZooKeeper协调服务优势的分布式应用的开发者准备的.本文包含理论信息和实践信息. 本指南的前四节对各种ZooKeeper概念进行较高层次的讨论.这些概念对于理解ZooKeep ...
- [CLR via C#]26. 计算限制的异步操作
一.CLR线程池基础 前面说过,创建和销毁线程是一个比较昂贵的操作,太多的线程也会浪费内存资源.由于操作系统必须调度可运行的线程并执行上下文切换,所以太多的线程还有损于性能.为了改善这个情况,CLR使 ...
随机推荐
- Luogu P5309 [Ynoi2012]D1T1
小清新分块题,从头到尾都十分套路,和CXR大佬一起YY了一下就出来了 首先套路地设个阈值\(S\),把修改的\(x\)分成大于\(S\)和小于等于\(S\)两部分,然后我们考虑分别求解这两部分 \(x ...
- ASP.NET Core 身份验证(一)
前言 这篇文章我想带领大家了解一下 ASP.NET Core 中如何进行的身份验证,在开始之前强烈建议还没看过我写的 Identity 系列文章的同学先看一下. Identity 入门系列文章: Id ...
- IM开发者的零基础通信技术入门(二):通信交换技术的百年发展史(下)
1.系列文章引言 1.1 适合谁来阅读? 本系列文章尽量使用最浅显易懂的文字.图片来组织内容,力求通信技术零基础的人群也能看懂.但个人建议,至少稍微了解过网络通信方面的知识后再看,会更有收获.如果您大 ...
- 【Android Studio安装部署系列】二十、Android studio如何将so文件添加到svn中
版权声明:本文为HaiyuKing原创文章,转载请注明出处! 概述 在使用android studio的过程中,常常会遇到一个头疼的问题,添加的so,居然无法被svn添加. 选项都是灰的: 那这种问题 ...
- Android多Module下的Application引用方式
版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 Android开发时,Application一般都放在APP中,Lib模块如果想引用Application则需要在APP中进行传递, ...
- springboot~zuul实现网关
网关在微服务里的角色 在微服务架构体系里,网关是非常重要的一个环节,它主要实现了一些功能的统一处理,包括了: 统一授权 统一异常处理 路由导向 跨域处理 限流 实践一下 1 添加依赖 dependen ...
- 系列文章|OKR与敏捷(二):实现全栈敏捷
OKR与敏捷开发的原理有着相似之处,但已经使用敏捷的团队再用OKR感觉会显得多余.这种误解的根源就在于对这两种模式不够了解,运用得当的情况下,OKR和敏捷可以形成强强联合的效果,他们可以创造出以价值为 ...
- Jetson Nano Developer Kit
The Jetson Nano Developer Kit is an AI computer for learning and for making. 一个推理框架,用于部署模型到嵌入式设备. ...
- shared_ptr和动态数组
std::shared_ptr智能指针是c++11一个相当重要的特性,可以极大地将开发者从资源申请/释放的繁重劳动中解放出来. 然而直到c++17前std::shared_ptr都有一个严重的限制,那 ...
- Linux高级运维 第四章 文件的基本管理和XFS文件系统备份恢复
4.1 Linux系统目录结构和相对/绝对路径 4.1.1系统目录结构 在windows系统中,查看文件先进入相应的盘符,然后进入文件目录 在windows中,它是多根 c:\ d:\ e ...