尽量不要编写返回值类型为void的异步方法
在通常情况下,建议大家不要编写那种返回值类型为void的异步方法,因为这样做会破坏该方法的启动者与方法本身之间的约定,这套约定本来可以确保主调方能够捕获到异步方法所发生的异常。
正常的异步方法是通过它返回的Task对象来汇报异常的。如果执行过程中发生了异常,那么Task对象就进入了faulted(故障)状态。主调方在对异步方法所返回的Task对象做await操作时,该对象若已处在faulted状态,系统则会将执行异步方法的过程中所发生的异常抛出,反之,若Task尚未执行到抛出异常的那个地方,则主调方的执行进度会暂停在await语句这里,等系统稍后安排某个线程继续执行该语句下方的那些代码时,异常才会抛出。
总结一句话就是:void的异步方法发生异常时,开发者得不到任何通知,程序既不会触发普通的异常处理程序,也不会把这些异常记录下来。总之,这会让相关的线程默默的终止掉。
不要把同步方法与异步方法组合起来使用
用async关键字来修饰的方法意味着该方法有可能会在执行完所有工作之前就把控制权返回给主调方,而且,它返回给主调方的是个代表工作进度的Task对象。主调方可以查询此对象的状态,以了解该工作是否已经完成、尚未完成还是在执行过程中发生了故障。此外,这种方法还在暗示主调方:本方法所执行的工作可能要花费很长时间,因此建议你先去做其他一些事情,稍后再来向我索要结果。
与此相反,如果把某个方法设计成同步方法,那么意味着当该方法执行完毕时,它的后置条件必定能够得到满足。无论这个方法要花多长时间去完成工作,它都会采用与主调方相同的资源来完成,主调方必须等这个方法彻底执行完毕才能向下执行。
这两种方法单独写起来都很清晰,但是如果把他们组合在一起就会让方法变得十分难用,而且有可能导致各种bug,如死锁。因此,这里提出两条重要的原则。第一,不要让同步方法必须等待异步方法执行完毕才能往下执行(尽量不用Wait()以及.result这些阻塞式的方法)。第二,不要让异步方法把虽然耗时很长、计算量很大但是完全可以由自己执行的工作转交给另一个异步任务去做。'
当然对于第二点,这并不是说计算量较大的任务绝对不能放在单独的线程中执行,而是说不应该把只用一个线程就能迅速做好的任务刻意的拆解成许多个较小的部分,并把他们分别放在多个新的线程上执行,而是应该把整个任务都交给某个线程来执行才对。
使用异步方法时应尽量避免线程分配
异步任务看上去好像很神奇,因为这种任务刻意转移到另一个地方去做,使得开启这项任务的异步方法可以在该任务完成之后,从早前暂停的地方继续往下推进。不过,要想发挥异步任务的功效,就必须保证把这项任务交出去确实能够少占用一些资源,而不是仅仅会在相似的资源之间进行上下文切换。










