很多编程工作都设计启动并回应异步任务。如分布式程序以及运行在多台计算机或虚拟机的程序。这些程序可能要跨越多个线程、进程、容器、虚拟机或物理机。然而异步编程并不等同于多线程编程。这个大家应该要了解。异步编程是开发者必须掌握的一个技能。今天主要和大家分享一下基于任务的异步编程的优化方式。
异步方法处理方式1、使用异步方法执行异步操作
异步方法可以更轻松地构建异步算法。这里我们来了解下其核心机制。编译器在处理异步方法时,会构建一种机制,该机制可以启动await语句所要等候的那项异步工作,并使得该程序在该工作完成之后,能够使用某条线程继续执行await语句下方的那些代码。这个await语句正是关键所在。编译器会构建相应的数据结构,并把await语句之后的指令表示等委托,使得程序在处理完那项异步工作以后,能够继续执行那些指令。待到await方法执行完之后,通过调用委托再更新相应的返回值的状态信息。
2、不要编写返回值类型为Void的异步方法
正常的异步方法是通过它返回的Task来汇报异常的。如果执行发生异常,那么Task进入故障状态。主调用方在异步方法所返回的Task对象做Await操作时,该对象若已处在故障状态,则系统将会抛出异常。如果主调用方法不是正常的异步方法,而是返回值类型为void的异步方法,那么它就无法捕捉或传播该方法在执行过程中所抛出的异常。因此,不应该把异步方法的返回值设计成void,那样会使主调方看不到其中所发生的错误。
3、不要把同步方法和异步方法结合起来使用
异步方法可能在执行完所有的工作之前就把控制权交给主调用方,同步方法调用时,必须满足它的所有前置条件,才能彻底的向下运行。这两种方法单独写起来很清晰,但是如果组合在一起,就可能发生各种问题。如有可能需要用另一种方式来处理异常、有可能发生死锁(尤其是在Web程序和GUI程序中)、有可能浪费资源。为此,这里应遵循两条原则。第一,不要让同步方法必须等待异步工作执行完才能往下运行。第二,不要让异步方法把虽然耗时长、计算量很大但是完全可以自由自己执行的工作转交给另一个异步任务去做。
4、使用异步方法以避免线程分配和上下文切换
异步任务看上去很神奇,因为这种任务可以转移到另一个地方去做,使得开启这项任务的方法可以在完成该任务之后,从早前暂停的地方继续往下推进。不过,要想发挥异步任务的功效,就必须保证把这项任务交出去之后确实能少占用一些资源,而不是仅仅会在相似的资源中进行上下文切换。
5、避免不必要的上下文编组
如果一段代码可以放在任意的synchronizeContext中执行,那么我们称其为上下文无关代码,若只能在特定的上下文执行,则称其为上下文相关代码。我们开发的大多数代码其实是上下文无关的。但是GUI应用程序中和UI交互的代码以及Web应用程序中跟HTTPContext及相关的类进行交互的代码则属于与上下文相关的代码。为了让用户能够顺畅的使用程序,我们可以调整代码结构,把上下文相关代码在await语句那里调用,使得程序可以把该语句下面的代码默认放在上下文中运行,而不用切换回早前的上下文。
6、通过Task对象来安排异步工作
Task是一种抽象机制。可以用来表示某项工作,于是就能够把该工作交给其他资源去完成。Task类型以及与之相关的类与结构体提供了丰富的API,让我们可以操控Task对象以及有该对象所表示的工作。并且,Task还有一些属性和方法,可以用来操控本对象所表示的任务。这些Task对象还可以合成起来构成一项比较大的任务,可串行和并行。通过Task去操控异步任务,可以大大优化你的代码,并且提高效率。因此当你需要安排异步工作时,可优先考虑Task来实现。
7、考虑实现任务取消协议
异步编程模型提供了丰富的写法,使得开发者可以启动任务、取消任务或者监控其进度。这些功能让我们可以根据底层的异步工作所具备的实际功能来设计相应的API。如果你要执行的异步任务能够支持这些功能,那么可以考虑提供与取消任务或者汇报进度有关的API,也可以同时把这两种API都实现起来。反之,若是异步任务工作本身不支持这些功能,那就不要实现了。
8、缓存泛型异步方法的返回值
在最新版的.NetFrameWork中提供了一种新的类型,叫做ValueTaskT,它用起来可能会比普通的Task更高效。该类型是值类型,因此,创建这种类型的对象时,不需要再分配额外的空间。这项因素使得我们可以创建多一些这样的对象,而不用担心它会像Task对象那样占用过多的资源。如果你的异步方法可以根据早前缓存的结果直接返回相应的值,那么优先考虑把返回值的类型设置为ValueTaskT。
以上关于基于任务的异步处理方式,可以大大的提高异步编程效率。作为一个技术人员,提高效率是我们孜孜不倦的追求。同时,坚持分享也是我的小目标。如果对你有用,欢迎收藏和