广义来讲,两个工作流能同时进行就算异步,例如,cpu与外设之间的工作流就是异步的。在面向服务的系统中,各个子系统之间通信一般都是异步的,例如,订单系统与支付系统之间的通信是异步的,又如,在现实生活中,你去馆子吃饭,工作流是这样的,点菜->下单->做你的事->上菜->吃饭,这个也是异步的,具体来讲你和厨师之间是异步的,异步是如此重要,因外它代表者高效率(两者或两者以上的工作可以同时进行),但复杂,同步的世界简单,但效率极极低。
在编程中,除了同步和异步这两个名词,还多了一个阻塞和非阻塞,其中,阻塞和非阻塞是针对线程的概念,那么同步和异步是针对谁呢?其实很多情况下同步和异步并没有具体针对某一事物,所以导致了针对同步阻塞、同步非阻塞、异步阻塞、异步非阻塞这几个概念的模糊不清。并且也确实没有清晰的边界,请看以下例子:
假设运行该代码的cpu是单核单线程,那么请问?doworka()、doworkb()这两个函数是异步的吗?因为cpu是单核,所以根本不能同时运行两个函数,那么从这个层次来讲,他们之间其实是同步的,但是,现实的情况是我们一般都认为他们之间是异步的,因为我们是从代码的执行顺序角度考虑的,而不是从cpu本身的工作流程考虑的。所以要分上下文考虑。再请看下面这个例子:
从代码的执行顺序角度考虑,这三个函数执行就是同步的,但是,从cpu的角度来讲,数据库查询工作(另一台机器)和cpu计算工作是异步的,在下文中,没有做特别申明,则都是从代码的执行顺序角度来讨论同步和异步。
再解释一下阻塞和非阻塞以及相关的知识:
阻塞特指线程由运行状态转换到挂起状态,但cpu并不会阻塞,操作系统会切换另一个处于就绪状态的线程,并转换成运行状态。导致线程被阻塞的原因有很多,如:发生系统调用(应用程序调用系统api,如果调用成功,会发生从应用态->内核态->应用态的转换开销),但此时外部条件并没有满足,如从socket内核缓冲区读数据,此时缓冲区还没有数据,则会导致操作系统挂起该线程,切换到另一个处于就绪态的慈溪浒山中学线程然后给cpu执行,这是主动调用导致的,还有被动审计方案导致的,对于现在的分时操作系统,在一个线程时间片到了之后,会发生时钟中断信号,然后由操作系统预先写好的中断函数处理,再按一定策略(如线程优先级)切换至另一个线程执行,导致线程被动地从运行态转换成挂起状态。
非阻塞一般指函数调用不会导致执行该函数的线程从运行态转换成挂起状态。
在此之前,我们先稍微了解下图形界面的工作原理,gui程序大概可以用以下伪代码表示:
其中dispathermessage根据不同的消息类型,调用不同的消息处理函数,例如鼠标移动消息(moumove),此时消息处理函数可以根据moumove消息中的值,做相应的处理,例如调用绘图相关函数画出鼠标此刻的形状。
一般来讲,我们称这个循环为消息循环(事件循环、eventloop),编程模型称为消息驱动模型(事件驱动),在ui程序中,执行这部分代码的线程一般只有一个线程,称为ui线程,为什么是单线程,读者可以去思考。
以上为背景知识。现在,我们思考,假如在ui线程中执行一个会导致ui线程被阻塞的操作,或者在ui线程执行一个纯cpu计算的工作,会发生什么样的结果?如果执行一个导致ui线程被阻塞的操作,那么这个消息循环就会被迫停止,导致相关的绘图消息不能被相应的消息处理函数处理,表现就是ui界面“假死”,直到ui线程被唤起。如果是纯cpu计算的工作,那么也会导致其他消息不能被及时处理,也会导致界面“假死”现象。如何处理这种情况?写异步代码。
我们先用控制台程序模拟这个ui程序,后面以此为基础。
上面那个例子,一但外部有消息到来,根据不同的消息类型,调用不同的处理函数,如鼠标移动时产生mou_down消息,相应的消息处理函数就开始重新绘制鼠标的形状,这样一但你鼠标移动,就你会发现屏幕上的鼠标跟着移动了。
现在假设我们增加一个消息处理函数,如onmou_down,这个函数内部进行了一个阻塞的操作,如发起一个http请求,在http请求回复到来前,该ui程序会“假死”,我们编写异步代码来解决这个问题。
此时界面不再“假死”了,我们看下代码可读性,感觉还行,但是,如果再在回调函数里面再发起类似的异步请求呢?(有人可能有疑问,为什么还需要发起异步请求,我发同步请求不行吗?这都是在另一个线程里了。是的,在这个例地理事物子里是没问题的,但真实情况是,执行回调函数的代码,一般都会在ui线程,因为取得结果后需要更新相关ui组件上的界面,例如文字,而更新界面的操作都是放在ui线程里的,如何把回调函数放到ui线程上执行,这里不做讨论,在.net中,这跟同步上下文(synchronization context)有关,后面会讲到),那么代码会变成这样
写过js的同学可能很清楚,这叫做“回调地狱”,如何解决这个问题?js中有promi,而c#中有task,我们先用task来写这一段代码,然后自己实现一个与task功能差不多的简单的类库。
是不是感觉清爽了许多?这是编写异步代码的第一个跃进。下篇将会介绍,如何自假药的定义己实现一个简单的task。后面还会提到c#中async/await的本质作用,async/await是怎么跟task联系起来的,怎么把自己写的task库与async/await连结起来,辽东学院是几本以及一个线程如何实现异步io。
到此这篇关于c#异步编程由浅入深(一)的文章就介绍到这了,更多相关c#异步编程内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!
本文发布于:2023-04-06 01:03:52,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/c58053358e024c3d18dc8ad49b8d2c17.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:C#异步编程由浅入深(一).doc
本文 PDF 下载地址:C#异步编程由浅入深(一).pdf
留言与评论(共有 0 条评论) |