在使用异步方法中最好不要使用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
对于一些预先知道的结果或者只是一个简单的计算函数,使用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包中
ValueTask相关文章
ValueTask相关文章
长时间运行的工作是指在应用程序生命周期执行后台工作的线程,如:执行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) { } }