扯淡异步
之前甚至看到有人调侃在捣鼓泛型约束的人说现在时4.0了,不要再捣鼓2.0的泛型了,要与时俱进开始整异步。当然本人有喷子的潜质,所以我就喷了那个人说异步的概念远比泛型早多了,可以算是古董了。
因为现在做C++,对C#这边倒渐渐生疏了下来,尤其4.0,很多东西都是靠浏览园子里面的文章来补充知识。所以到现在我也不知道为什么4.0了异步就开始热火了,毕竟这个真的是古董级别的,一点都不比云计算的概念来得年轻,都不知道是上个世纪哪个旮旯年代就被提出来了,云计算一直因为种种原因基本与世隔绝,直到当下取了个洋气的名字才开始风行。而异步却很早就渗入到了程序员的级别,无论linux还是windows上C++的编程,异步是一个很正常的处理问题的方法。
下面就转入正题,说说C#的异步吧,也赶一会潮流,虽说到现在我也不知道为什么异步现在如此火热,园子里首页基本天天看到异步。
要讲异步不得不首先提到同步,就像说一个人的性格,不得不扯上他得胞兄,虽说他的胞兄很无辜,但这个世界上有些事情总要有些人做些陪衬,即使他本来可能更出色。
有人说同步就是对讲机,异步就是电话机。但我总觉得类比是个蛋疼的玩意,毕竟即使B与A真的很像,但毕竟不同,B永远不是A。一类比就意味着你已经漏了很多原汁原味的东西,更坏的结果就是类比让人开始了南辕北辙,缘木求鱼。所以我还是建议能看英文的就尽量看英文,再不济你看看msdn也行,对于windows下的开发而言,msdn真的是奢侈品,等哪天你遇到问题你不得不去查看源码,而没有帮助手册的时候你才会发现啊,原来曾经我们生活在天堂啊。当然微软的源码问题也一直被诟病,毕竟他很多都不开源。
下面解释下关于同步异步的定义吧。
同步:等待函数执行后返回
异步:不等待函数的执行而直接返回
相信大家小学的时候都学过统筹方法吧,华罗庚那个,说烧开水的。那篇文章讲得就是异步。忘记了也没关系,反正我要改编。
比如,想泡壶茶喝。当时的情况是:开水没有;水壶要洗,茶壶茶杯要洗;火生了,茶叶也有了。怎么办?
办法甲:洗好水壶,灌上凉水,放在火上;等待水开,水开了后去洗茶壶、洗茶杯、拿茶叶;泡茶喝。
办法乙:洗好水壶,灌上凉水,放在火上;烧水的同时去洗茶壶、洗茶杯、拿茶叶;等水开,泡茶喝。
看完这个,应该很清楚什么叫同步什么叫异步了。 同步就是你干事一个个顺着干,干完这个干那个。异步就是你把这个安排下然后就去干别的事了,而这件事就被你安排给别人去干了,你只问结果。(当然实际上你是可以不问结果的,对于有些事情,假如你不在意其结果的话。) 也许你会问我,第二种模式下假如在洗茶杯的时候水开了咋办啊,其实不怎么办,直接去把火关了,然后再去洗茶杯。当然这时你又会问我,那我洗茶杯的时候,我怎么知道水开了啊?
这里面涉及一个观察者模式,别怕,这些神马模式都是扯淡的浮云,我说了具体实现方法估计你又要雨停了,天晴了,你又觉得你行了。其实在C#里面有一个东西叫事件,当然java也有啊,这里只是局限于园子是C#的(主要是C#的)就拿C#说事了。水烧开了,你直接触发那个事件就OK了,而我,也就是这里面的主角,事先注册了这个事件的响应方法就行了,这里就是去关火。
又扯远了,下面继续回来扯同步异步。
相信此时你对什么是同步什么是异步很清楚了。
C#下面最简单的同步就是你写的最基本的顺序方法,做事一个跟着一件,后来遇到跨线程调用UI线程的东西,你发现C#竟然不准跨线程干这个事情,于是msdn又告诉了你一个方法Invoke。于是空气又清晰了。
异步,后来你发现有时候有些事情真耗时,而我可不想给用户这个体验,连CPU都知道决不让自己闲着,那么我们更不能让线程阻塞着。于是我们想到了异步。于是msdn又告诉了我们BeginInvoke,当然顺带你又瞟到了EndInvoke与IAsyncResult。于是你学会了这东西怎么用了。
现在我想问的是,这两个东西他们开销的资源是谁的啊?
同步你理所当然认为是当前线程的,异步你就开始迷糊了。
上面的例子中,无论是同步还是异步,烧水这个事情开销的都是煤气灶的资源,是他在烧水,而不是你在烧水。这个时候就要看我们系统的边界了,如果我们的边界是人、煤气灶、水壶、水、茶叶等等所有资源,那么开销的都是系统的资源。无论同步还是异步,只是异步能让当前线程解放出来,而从线程池里面找了个煤气灶出来,让其负责去烧水。而同步的状况下就是当前线程在傻傻的烧水,从用户体验来说,异步明显好过同步,因为当前线程不会阻塞在那个傻×点上面。但线程池的毕竟也是线程,线程的切换需要开销资源,所以异步相对而言多开销资源了。
我们编程也一样,组件多了或者线程多了必然带来哪些不必要的浪费。但有时候这种浪费是必要的,而且是经济的。
上面说的系统边界包括了所有,第二种就是系统边界就是人,那么烧水开销的就是外部资源了,这个时候同步的话,系统是不开销烧水的资源,但却要傻傻的开销等水烧开的资源,于是实际上系统是开销了烧水的时间资源,当然这里不会包括空间资源。但异步的话,却只开销一个水烧开后的泡茶叶的资源,而不必开销那个傻等的资源。但这里面涉及一个通信也就是水壶需要告诉你水开了这个消息,这里面就涉及到了回调函数,于是就涉及到了另一个资源开销,系统态、用户态切换的资源消耗,但通常情况下和等水烧开开销的时间资源完全不是一个档次。所以此处异步相对同步来说,好处已经不是一两点了。
也许你也发现了我上面在玩弄文字游戏,于是你觉得这个作者真是个不懂技术的人,只会扯淡。恩,我的确很喜欢扯淡,不然怎么扯出一篇文章啊。
但如果你自己独立做过几个系统你会发现好像又有点道理,不是纯粹扯淡。
在我们做简单的管理系统的时候以及一些简单单机app的时候,不涉及外设,我们所遇到的东西都是我们系统内的,这个时候异步纯粹是为了交互体验,总资源消耗并不会真的变少,因为这里的异步其实就是个多线程而已,减少的是时间资源,但也增加了空间资源。
但假如我们做的涉及台位之间通信的app,那么这个时候对于通信部分的异步或者和外设之间的异步就不再是多线程那么简单了。如socket的异步重叠或者IOCP,煤气灶已经是外部资源了,煤气灶烧水并不会增加CPU的负载,只有等到谁烧开了通知CPU,CPU才会开始消耗资源,也就是泡茶叶。
相信很多人都对异步捣鼓过,都想过这东西用多线程不也能实现异步。但线程的创建和易做图都是消耗CPU的,我们没事还是别折磨CPU的好。所以虽然我们上面说的第一种系统边界,微软也用了线程池这个概念,为的就是别折磨CPU了,毕竟他是我们的老祖宗,你折磨他,他就折磨你的用户,反过来你的用户就要来折磨你了。对于第二种系统边界,千万别没事用多线程去模拟异步,那更是闲的蛋疼,小心你的用户不付你钱。
异步的东西就扯这么多了,文章里面有些东西可能存在问题,很多都是我自己的理解,所以估计出问题的概率较大,还望阅读此文的你对其中错误予以指正,对于不足予以补充。
作者BLoodMaster
补充:软件开发 , C# ,