何为异步以及如何实现异步(C#)

说明:本文只讨论同步异步,以及异步的多线程实现,不讨论多线程导致的数据不同步问题.

同步和异步是相对的  

在异步概念出来之前,我们的代码都是按同步的方式写的。简单来说,就是程序严格按照代码的逻辑次序,一行一行执行。一行代码不走完不会执行下一行代码,当一行代码 特别耗时时就会造成阻塞,等待,造成的现象就是卡死.

异步就是为了解决这种阻塞而产生的,它是一种功能要求,说异步是功能要求意味着它的实现可以有多种形式,只要解决阻塞的问题就可以了.


举例:

1.异步 I/O操作

I/O操作非常耗时,但是在硬件层面上 硬盘,显卡和内存是可以在不耗费CPU资源的情况下进行数据交换的.所以CPU可以控制硬盘和内存之间的开启,且不用等待,当资源交换完毕后回调给CPU就可以了 ,这就实现了异步,I/O流的异步操作是最常见的异步操作之一.

2.Unity中的协程(异步的单线程实现)

Unity中的Coroutine旨在将一个函数分多次执行,从而实现等同并发的处理方式,Coroutine可以用来实现异步,比如计时器(WaitForSeconds()),此外继承CustomYieldInsruction可以实现更丰富的异步效果.

实现了计数100个 达成逻辑往下走的条件. 





3.异步不是多线程

前面说到异步是一种功能要求,并且举例了I/O流的异步和Unity协程的异步,I/O流的异步是硬件属性实现的异步,Unity协程则是在主线程中对给定条件的判断 决定是否执行下一行代码,而多线程则是另外一种异步的实现方式,因为异步和多线程总是被同时提到,且有迷惑性,所以又强调了下这个问题.



C#中使用多线程实现异步


1.用C#语言提供的多线程实现异步,这里使用Task来实现,主要使用await 和async. (主要原因是老版本的.net 多线程不太好懂,最初需要自己申请Thread,后来衍生出ThradPool允许开发者申请线程池,但是还不够好用).


Thread.Sleep()用来模拟耗时操作

Thread.CurrentThread.ManagedThreadId表示CLR提供的虚拟托管线程Id(用来判断当前方法在哪个线程被执行)


先实现一下同步吧(不是说大家不会哈)....用作和下文异步的对比


同步代码

结果为:



异步的多线程实现:

这里需要解释下await关键字和async关键字

async用来修饰 方法,表示该方法可能会采取异步的方式运行,修饰的方法必须void 或返回Task,Task


await用来修饰 Task,Task ,当程序运行遇到await时会跳出当前方法继续执行主线程中的方法,并安排持续监听它修饰的Task是否生成了结果,一旦生成结果,则再调回来继续执行修饰的Task下一行的代码.并且可以用Task.result访问到它的结果


程序进入await修饰的Task中后,如果方法中不存在await 则当前是在同步执行,执行完该方法时,接着await后面执行. 如果修饰的Task中存在await 则在当前线程中又申请了新的线程处理await后面的Task,这是一个循环的过程.

await修饰的Task中没有await 则当前执行的是同步方法反之 await修饰的task是个异步Task ,则它就是异步方法 ,那么如何声明一个异步的Task呢?

声明一个异步的Task:

Task.Run();(多线程编程中有很多方法声明异步Task,这里就放个最快实现的)

Task.Run的内部代码会占用线程池资源,并在一个可用的线程上与主线程并行运行。


该代码每个async修饰的方法中都有await,遇到await就跳出,task执行完毕就回来,所以Console.WriteLine(${“退出第{index}个方法”})一定是一个AsyncFunction中最后一个执行的.结果为


若将上文的SimulateLongTimeFunction (index); await修饰去掉 则   Console.WriteLine($"退出第{index}个方法");是for循环中每一轮结束的最后一句打印

如下图:


可以看出耗时任务是最后执行完毕的,别的代码没有await直接就跑完了

说完了await就要提到另一个词wait ,二者特别容易混淆,所以我想说完同步异步和await之后再提wait的事  上文中模拟的耗时任务总是最后才完成.假如我要使用他们的结果,就必须等待他们都处理完毕 就可以用Task.Wait来等待结果,该方法是一个同步方法 ,所以它是阻塞的.

多线程编程会带来数据冲突的问题,尽管可以通过lock的方式避免数据冲突 但是,lock开销比较大.


2.Unity开发中还可使用Unity提供的JobSystem实现异步

很多功能C#实现一次,Unity在其基础上结合自己的使用场景又封装一次这种操作还蛮常见的,比如C#的event和Unity的unityEvent等等.JobSystem的最小单位是Job,嗯听起来和Task差不多...

Unity号称JobSystem帮开发者解决了很多痛点,比如前面说到的多线程数据冲突的问题? 我也没细研究过...

你可能感兴趣的:(何为异步以及如何实现异步(C#))