关于Web开发里并发、同步、异步以及事件驱动编程的相关技术
一、开篇语
我的上篇文章《关于如何提供Web服务端并发效率的异步编程技术》又成为了博客园里“编辑推荐”的文章,这是对我写博客很大的鼓励,也许是被推荐的原因很多童鞋在这篇文章里发表了评论,有童鞋说我这篇文章理论化很严重,没有实际代码和具体项目做支撑,这个评论让我有种理论和实践脱节的味道,所以我想在这里谈谈我为什么要写这篇文章的原因,这篇文章是把我前不久学习多线程编程的一个总结。
当我从我书堆里找到所有与多线程开发相关的书籍简单阅读后,我发现了一个问题,在java里开发多线程最强有力的实践就是做服务端的并发处理,其他的应用都是小儿科,而这个实践对于每个web程序员而言都是极具经济价值的,所以我就想延着这个实践继续学习多线程,接着我发现并发处理这块都离不开IO处理,同时IO处理的优劣直接关系着并发处理的效率问题, 关于并发的知识,有人说最好是写写代码,告诉我们API如何运用,说实话简简单单写几行不是太难的事情,写几行代码并不代表你能掌握它,掌握多线程绝不是集中精力看懂几行代码,看懂后顿悟的来一下:哦,明白了。
要真的掌握某种技术你就必须要知其所以然,但是要理清并发的所以然就不那么容易了,这其实是一种设计的思想,而不是简单几行代码,几个jdk提供的api,代码和api只不过是实践这些思想的实现,我之前学习线程开发,学习io的api,这个过程很早就开始,但这个过程都基本都是记住遗忘再记住再遗忘的死循环,究其原因就是因为它们本身的设计思想太深奥,你没有理解这个设计思想,你就无法将那些api的内在关系串联起来,所以最终的结果只会是遗忘。技术的本质就是工具,要用好工具的前提就是要真正理解这个工具的价值,这就好比斧头设计出来时用来砍柴的,菜刀设计出来是为了切菜,如果我们用斧头用来切菜,菜刀用来砍柴,这肯定要闹笑话了,对于像线程和IO这样复杂的工具,使用起来和使用像这种简单工具菜刀和斧头一样,应该是物尽其才,各司其职,复杂工具的麻烦就是理解起来比较困难些了,自己得多花点时间和功夫了。
我早期的文章都会写好多代码,但是最近的文章我会写更多文字,我希望对我熟悉的技术有更进一步的理解,因此理论和猜想会更多点。
本篇文章的标题是《关于Web开发里并发、同步、异步以及事件驱动编程的相关技术》,本文的目的就是要和大家一起好好的理解下标题里很常见的概念,通过理解它们,指导我们今后对这些概念的学习和运用。
二、我们来好好理解下并发、同步和异步这三个概念
标题的三个概念在web开发领域太常见了,就算是入门web开发的童鞋随便找本web开发的书籍都能找到这三个概念,但是如果你对这三个概念理解不够透彻,如果在实际开发中正好碰到三个概念的交织在一起的问题,估计很多人就会麻了头。
并发
首先是并发这个概念,我们看看现实中并发的例子。
第一个例子:
有一天你在家里做饭,你这天的心情非常好,想大吃一顿,所以要有菜有汤还有酒喝,开始干了,你首先会在电饭煲里把饭煮好,饭在煮的时候,你会抽空去超市买瓶啤酒,买好了啤酒回到家里你就要做菜了,你想做个汤,为了节约时间,你会先烧开水,烧开水同时,你会切菜,切好的菜就得炒菜,后面的过程此处省略一万字了,地球人我想都明白以后是咋样了,我就不再详细讲述了。
上面的描述里煮饭、烧水、买啤酒、切菜、炒菜,它们都可以当作独立的任务,这些任务都是为了达到某种特定的目的,它们之间是不能相互替代替换的,但是在实际操作里你并不是等待某个任务做完了再去做另一个任务,而是会运用统筹方法,交叉进行,这么做的好处就是效率很高,节约时间,这和程序员平时工作类似,一边编写程序,一边听歌,一边和朋友同事在QQ里聊天。
第二个例子:
现实生活里还有一种并发,例如我们在食堂里吃饭,去食堂吃饭的人很多,因此食堂开启了好几个打饭的窗口,如果打饭的窗口开的越多,那么吃饭人打到饭所用时间也就最少,如果每个食客都有一个窗口一对一的服务,那么食客基本就不用浪费任何时间去等待了,这种vip感在中国这么资源紧张的国家里,简直太爽了,食堂打饭的并发就和我上篇文章里讲到的web服务端并发类似。
这里有列举了两个并发的实例,似乎有点多此一举之嫌,但是如果我们仔细研究下这两个例子,它们其实有本质的不同,第一个例子其实是一个人的并发,第二个例子就是多人的并发,用计算机的概念描述就是前一个例子就是一个线程完成的并发,后一个例子就是多线程完成的并发,第一个例子的线程载体就是做饭的人,第二个线程的载体就是每个打饭窗口的工作人员,这两个例子其实就可以类比到我上篇文章提到的做法二和做法三,特别是第一个例子,虽然这个场景人们非常熟悉,但是到了程序开发里却有很多人把第一个实例并发给遗忘掉,特别是做web开发的人,并发就是多线程,一个线程怎么可能做并发了,但是例子一说明一个线程是可以做并发的。
由上面的描述,下面我要给出我自己总结的并发定义:
并发就是让一系列独立的任务同时执行,同时执行包含两种情形,一种就是完全独立执行,例如在拥有双核处理器的计算机里,每个CPU在同一个时间里处理不同的任务,另一种情形是一个任务还没执行完,而CPU计算被闲置的时候CPU用来处理别的任务。
而现实里我们要做到高效的并发就得把两种模式的并发混合起来使用,食堂打饭就是混合了两种模式的并发,对于计算机两种混合的并发才能把计算机的计算能力深度挖掘,所以做法三就是混合了两种并发,做法三是高效和先进的,做法三是值得推广的。
接下来就要谈谈同步和异步两个概念了,记得刚工作不久,有次参加面试,面试官问我什么是同步什么是异步,我当时的回答是:同步就是单线程,异步就是多线程,当时那位面试官停顿了下,跟我说这个解释没有问题,但是在实际开发里这个答案不能让你把同步和异步用的很好。现在回忆那位面试官给我答案的评定还是有道理的,下面我开始讲同步和异步了。
同步:
在计算机领域同步应该是指相互独立的任务一个接着一个运行,拿上面做饭的例子描述就是煮饭煮好了才能去超市买啤酒,酒买好了才能切菜,菜切好了才能炒菜,菜炒好了才能烧水烧汤,明显同步的方式在现实生活里效率是很低的,不过同步有个很大的好处就是简单,简单的理论基础就是一次就做一件事情,做的时候一心一意专心致志。
异步:
下面就是异步的概念,同步和异步在当代的计算机领域里是两个对等的概念,但是如果我们要追溯异步的本质,就会发现同步和异步其实是一个因果关系,同步是因,异步是果,换个说法同步就是异步这个孩子他娘,所以我们要正确理解异步就要明白同步做不好的事情,在web前端有个鲜活的例子可以说明这个问题,这个例子就是ajax,在ajax出现前,浏览器的一个独立页面和服务端的交互是通过一个socket进行的,这个socket相当于一个线程,它是采用同步处理的方式,所以没有ajax的时代,单独页面每次和服务端的交互都只能做一件事情,而网络处理的速度往往是最慢,而且网络的效率很容易受到外界影响,因此同步请求会导致用户使用页面的体验很不好,同步网站时刻都在考验着用户的耐心,这就导致ajax的出现,ajax本质就是浏览器提供了一个新的socket链接,这个socket是有别于同步的socket,它可以独立于同步socket运行,有了ajax这个socket我们就可以在不影响页面同步操作的前提下也能从服务端获取数据,这就好比浏览器由原来一个人完成和服务端的交互变成了现在两个人完成和服务端的交互操作,它们各司其职,共同完成页面上的功能。
异步和并发的关系和区别:
异步操作和并发看起来很像,特别是和我前面讲到并发的做饭实例很像,的确并发和异步常常是交织在一起的,但是它们还是有很大的区别,这个区别在于它们所达到的业务目的,并发是业务含义是能做更多事情,而异步是让多个人共同完成一个任务,异步其实是通过专业角度把一个大任务拆分成相对独立小任务,让更加专业的人完成这个小任务,小任务完成后最后汇总成一个大任务的结果,上面ajax就是这样的道理,其实我以前着重研究的hadoop就是一个典型的异步任务系统。
异步和并发共同点都是通过多线程来实现,通过它们在业务场景的区别,我们反过来学习多线程,就知道多线程能为我们做好哪些事情,那么当你碰到需要使用多线程的业务场景就知道按什么思路来分析这个业务场景了。
三、关于事件驱动编程
在我上篇文章里我反复写了好多事件驱动这四个字,到了本文里事件驱动后面我加了两个字编程,为什么加它我后面会讲到。
全世界最熟悉事件驱动的程序员是哪种程序员?答案是前端工程师,不管是桌面前端还是web前端都是世界上最熟悉事件驱动的,以web前端为例,我们做页面可以不去想什么面向对象编程,什么jQuery框架咋用,但是为按钮,为页面元素添加相关事件操作肯定是不可缺少的,而web前端的事件处理机制就是标准的事件驱动机制,为了讲清楚事件驱动,这里我回顾下页面里事件机制,我们开发页面的事件时候,第一步就是定义事件(定义事件就是在定义一个函数)或者说为事件定义一个动作,并把事件绑定到指定的元素上,如果我们没有触发元素上的事件,那么定义好的函数也就不会执行,如果元素上的事件被触发了,定义好的函数才会执行。代码不提供了,这个太简单和平常了。
关于浏览器里事件机制实现方案,我找了许久都没有找到完整的资料,因此这里我大胆揣测下事件机制的实现方案,下面的内容完全是我的猜想,不一定和实际相符,具体如下:
首先我要说多线开发程里有一个经典的设计方法,这个方法就是生产-消费者模型,生产-消费模型特点就是生产者和消费者被一个中间队列分隔开来,不管是生产请求还是消费结果都是通过这个中间队列中转,这样就可以把生产者和消费者关系解耦,事件实现机制从宏观上和生产-消费模式类似,这个类似不是指设计思想,而是沟通双方联系的那个中间层。
事件处理的机制里应该有个事件处理器,事件处理器位于元素和事件处理方法的中间位置,我们在定义事件的时候就是等于在事件处理器里定义元素和事件处理方法的关系,当这种对应关系定义好后,事件处理器就会启动一个死循环,这个循环反复检测元素的状态变化,当事件处理器发现某个状态产生了变化,处理器就会找到对应的事件处理方法,然后执行这个方法。
Nodejs是一个事件驱动的语言,这是官方对nodejs的定义,很多评论说nodejs是第一个把事件驱动上升到语言层级的编程语言。所以本文我在事件驱动后面加上了编程两字。
传统语言做开发都是按时间先后顺序进行的,这么做既可以降低语言的学习成本,也让开发代码思路比较容易控制,但是现实场景是复杂的,这种按事件顺序的开发流程并不一定是我们解决现实问题的最佳方式。这好比我们做一件事情,在做的时候我们会碰到很多情况,由于发生的情况的不同,那么这件事情的结果可能就会因为情况不同而发生变化,如果按照时间顺序的编程方式想做好上面的事情会让程序变得十分复杂,因为我们要按照时间顺序做出各种不同执行路径,这就是排列组合的办法了,这显然让事情变得复杂了,如果用事件驱动编程方式,我们只要定义好事务的起因,各种不同的过程情况,以及所能得到结果,换句话说我们首先只要关注实体内容而忽略事务关系问题,而事务关系则是在事件处理器里定义的,当我们发送给事件处理器一个指示信号,处理器就会对应找到某个行为,那么事件驱动编程就简化了程序开发的流程。
事件驱动编程实现的核心技术就是能让方法变成对象能在事件处理的流程里传递,方法得到事件管理器的指令后在合适的位置上被促发,这就是回调函数,而javascript语言里函数可以当做对象传递,也就保证了事件驱动编程上升到语言层级变成了可能,我想这就是nodejs作者使用google的V8引擎设计出nodejs的重要原因之一。
回调函数改变了传统程序开发的流程,但是大量使用回调函数的代码常常会变得晦涩难懂,这也是javascript语言很掌握的重要原因之一。鉴于回调函数这个毛病出现了promise编程,promise的目的是让回调编程看起来像按时间顺序编程的方式,我前端时间研究了下promise,但是没有深究,原因是回调本来就很难理解,回调变成顺序编程,那岂不是更加糊里糊涂,按现在技术特点,我会选择慎用promise技术。
四、小结
本文的主要内容就到此为止,鉴于上篇文章有些内容有很大的争议,本文想做一定的解释说明:上文最大的问题还是IO的解释上,我承认自己对IO其实理解不太深入,所以我只是用文字描述非阻塞IO的处理,这段文字写的时候我还是很注意的,尽量不讲太多,我当时只用一个理念来写这个实现,就是非阻塞IO的具体实现里一定会有一个和事件处理器相类似的中间层来协调IO操作和CPU的操作,这点我自信不会有错。其实IO技术在java里相当复杂的,比较难学,现在jdk提供的IO的模型有三大类,BIO(阻塞IO),NIO(阻塞IO或者叫新IO)以及AIO(异步IO或者叫NIO2.0),jdk的io是建立在操作系统IO上的,所以学习IO真的要多花点心思和精力,这是我今后学习研究的一个重点。
此外,在计算机里不管执行什么任务都会包含IO操作和CPU计算两个过程,IO的速度问题常常拖累了CPU的计算,由于某些IO太慢了,如果站在CPU的角度,它等待IO处理的代价实在太高了,所以先进的IO就是为了调整IO处理和CPU计算的关系,我觉得新IO解决方案要解决的核心问题。
好了,本文写到这里,本文和上文类似都是谈自己对某些技术的理解,文字很多,几乎没啥代码,希望童鞋们多拍拍砖,有问题才能进步的更快。
关于Web开发里并发、同步、异步以及事件驱动编程的相关技术的更多相关文章
- Python并发编程系列之常用概念剖析:并行 串行 并发 同步 异步 阻塞 非阻塞 进程 线程 协程
1 引言 并发.并行.串行.同步.异步.阻塞.非阻塞.进程.线程.协程是并发编程中的常见概念,相似却也有却不尽相同,令人头痛,这一篇博文中我们来区分一下这些概念. 2 并发与并行 在解释并发与并行之前 ...
- web开发基础(同步更新中)
1/Get与Post的区别 GET是我们都熟悉的.它用于请求网页文本.当你在浏览器输入harvard.edu,它会直接访问Harvard的web服务器,去GET /. 第二个最有名的是POST,它经常 ...
- Web开发中,用到的4种会话跟踪技术
会话跟踪:主要解决HTTP的无状态问题,即: 当用户发出请求时,服务器就会做出响应,客户端与服务器之间的联系是离散的.非连续的.当用户在同一网站的多个页面之间转换时,根本无法确定是否是同一个客户,会话 ...
- 多道技术 进程 线程 协程 GIL锁 同步异步 高并发的解决方案 生产者消费者模型
本文基本内容 多道技术 进程 线程 协程 并发 多线程 多进程 线程池 进程池 GIL锁 互斥锁 网络IO 同步 异步等 实现高并发的几种方式 协程:单线程实现并发 一 多道技术 产生背景 所有程序串 ...
- 前端文摘:Web 开发模式演变历史和趋势
今天的<前端文摘>给大家分享一篇玉伯的文章.文章详细介绍了 Web 开发的四种常用模式以及未来可能成为流行趋势的 Node 全栈开发模式,相信你看了以后一定会有收获. 您可能感兴趣的相关文 ...
- Web 开发模式演变历史和趋势
前不久徐飞写了一篇很好的文章:Web 应用的组件化开发.本文尝试从历史发展角度,说说各种研发模式的优劣. 一.简单明快的早期时代 可称之为 Web 1.0 时代,非常适合创业型小项目,不分前后端,经常 ...
- [转] Web 开发模式演变历史和趋势
文章转自梦想天空--前端文摘:Web 开发模式演变历史和趋势 一.简单明快的早期时代 可称之为 Web 1.0 时代,非常适合创业型小项目,不分前后端,经常 3-5 人搞定所有开发.页面由 JSP.P ...
- Web开发模式演变(转)
add by zhj:目前用的最多应该是模式二,其次是三.四,而模式五比较新,我自己也不太懂. 模式二--前后台交互的方式为整个页面,即每次请求,服务器都将HTML模板渲染后发给客户端,每次请求都返回 ...
- Web开发必知的八种隔离级别
ACID性质是数据库理论中的奠基石,它定义了一个理论上可靠数据库所必须具备的四个性质:原子性,一致性,隔离性和持久性.虽然这四个性质都很重要,但是隔离性最为灵活.大部分数据库都提供了一些可供选择的隔离 ...
随机推荐
- sql报句柄无效。 (异常来自 HRESULT:0x80070006 (E_HANDLE))
是由于数据库连接资源被耗尽或者用完没被释放导致的. 我在字符串中加了启用连接池好了. 如果错误信息为:sql 无效操作.连接被关闭 也是这个问题导致的.
- iOS—如何申请苹果公司开发者账号流程详细图文介绍(包括邓白氏编码的申请方法详细介绍)
我们要申请开发者账号,首先就需要先注册一个苹果的apple id,然后再这个账号的基础上去继续,这个相信大家都知道 这是申请appleid的地址:https://appleid.apple.com/a ...
- 一般多项式曲线的最小二乘回归(Linear Regression)
对于一般多项式: K为多项式最高项次,a为不确定的常数项,共k+1个; 有离散数据集对应,其方差: β为,方差函数S对β自变量第j个参数的梯度(偏导数): 当以上梯度为零时,S函数值最小,即: 中的每 ...
- 如果因特网中的所有链路都提供可靠的交付服务,TCP可靠传输服务是多余的吗?
IP协议因为是无连接的, 所以其传输是不可靠的.虽然链路保证了数据包在端到端的传输中不发生差错,但是它不能保证IP数据包是按照正确的书需到达最终的目的地.IP数据包可以使用不同的路由通过网络,到达接收 ...
- iOS 单例模式
主要用于做应用程序的资源共享控制.用途很多⋯⋯ 实质为,单例是在程序声明周期里 有且仅有 被实例化过一次的类.为确保实例化的唯一,利用类的 类(static)方法来生成和访问对象. 至此,你便可以在程 ...
- 年月日 生日 js插件
<script typet="text/javascript" src="http://libs.baidu.com/jquery/1.9.1/jquery.min ...
- Java 反射工具类封装
封装了常用的反射相关方法 public class ReflectUtil { /** * 通过类路径获取Class * * @author LHY <br> * Description ...
- 使用KeepAlived搭建MySQL高可用环境
使用KeepAlived搭建MySQL的高可用环境.首先搭建MySQL的主从复制在Master开启binlog,创建复制帐号,然后在Slave输入命令 2016年7月25日 配置安装技巧: ...
- matlab资源
百度网盘 链接:http://pan.baidu.com/s/1c06ikEW 密码:9dpt包含matlab6.5,7,7.01,7.04,7.1,Matlab2006b(7.3),Matlab ...
- sql如何做递归层次查询
DROP FUNCTION IF EXISTS `WhlFucGetIDsOnID`; CREATE DEFINER = `root`@`127.0.0.1` FUNCTION `WhlFucGetI ...