为什么说js是js阻塞执行式io,举例说明

所有浏览器在下载JS的时候,会阻止一切其他活动,比如其他资源的下载,内容的呈现等等。直到JS下载、解析、执行完毕后才开始继续并行下载其他资源并呈现内容。为了提高用户体验,新一代浏览器都支持并行下载JS,但是JS下载仍然会阻塞其它资源的下载(例如.图片,css文件等)。
由于浏览器为了防止出现JS修改DOM树,需要重新构建DOM树的情况,所以就会阻塞其他的下载和呈现。
嵌入JS会阻塞所有内容的呈现,而外部JS只会阻塞其后内容的显示,
2种方式都会阻塞其后资源的下载。也就是说外部样式不会阻塞外部脚本的加载,但会阻塞外部脚本的执行。
CSS本来是可以并行下载的,在什么情况下会出现阻塞加载了(在测试观察中,IE6下CSS都是阻塞加载)
当CSS后面跟着嵌入的JS的时候,该CSS就会出现阻塞后面资源下载的情况。而当把嵌入JS放到CSS前面,就不会出现阻塞的情况了。
根本原因:因为浏览器会维持html中css和js的顺序,样式表必须在嵌入的JS执行前先加载、解析完。而嵌入的JS会阻塞后面的资源加载,所以就会出现上面CSS阻塞下载的情况。
嵌入JS应该放在什么位置?
1、放在底部,虽然放在底部照样会阻塞所有呈现,但不会阻塞资源下载。
2、如果嵌入JS放在head中,请把嵌入JS放在CSS头部。
3、使用defer(只支持IE)anysc W3C
4、不要在嵌入的JS中调用运行时间较长的函数,如果一定要用,可以用`setTimeout`来调用
Javascript无阻塞加载具体方式将脚本放在底部。&link&还是放在head中,用以保证在js加载前,能加载出正常显示的页面。&script&标签放在&/body&前。成组脚本:由于每个&script&标签下载时阻塞页面解析过程,所以限制页面的&script&总数也可以改善性能。适用于内联脚本和外部脚本。
非阻塞脚本:等页面完成加载后,再加载js代码。也就是,在window.onload事件发出后开始下载代码。(1)defer属性:支持IE4和fierfox3.5更高版本浏览器(2)动态脚本元素:文档对象模型(DOM)允许你使用js动态创建HTML的几乎全部文档内容。代码如下:
var script=document.createElement("script");
script.type="text/javascript";
script.src="file.js";
document.getElementsByTagName("head")[0].appendChild(script);
&这里相当于给head下面插入了一个script的标签。当script.src所对应的文件下载以后,再而后执行。
&此技术的重点在于:无论在何处启动下载,文件额下载和运行都不会阻塞其他页面处理过程。即使在head里(除了用于下载文件的http链接)。
阅读(...) 评论()下次自动登录
现在的位置:
& 综合 & 正文
JS学习笔记(一)——JS的阻塞特性
JS具有阻塞特性,当浏览器在执行js时,不能同时做其它事情,即&script&每次出现都会让页面等待脚本的解析和执行(不论JS是内嵌的还是外链的),JS代码执行完成后,才继续渲染页面。
由于,JS的这种阻塞特性,每次遇到&script&,页面都必须停下来等待脚本下载并执行,这会停止页面绘制,带来不好的用户体验。所以,有必要减少JS阻塞特性造成的困扰。
1 优化脚本位置
HTML4规范中,&script&可以放在&head&或&body&中。你可能习惯性的在&head&中放置多个外链JS、CSS,以求优先加载它们。浏览器在继续到&body&之前,不会渲染页面,所以,把JS放在&head&中,会导致延迟。为了提高用户体验,新一代浏览器都支持并行下载JS,但是JS下载仍然会阻塞其它资源的下载(eg.图片)。尽管脚本的下载过程并不会相互影响,但页面仍然必须等待所有JS下载并执行完成才能继续。显见,所有&script&应该尽可能放到&body&的底部,以减少对页面下载的影响。
注意:CSS文件本身是并行下载,不会阻塞页面的其他进程。但是,如果把一段内嵌脚本放在引用外链CSS的&link&之后会导致页面阻塞去等待CSS的下载。这样做是为了确保内嵌脚本在执行时能够获得正确的样式信息。所以,最好不要把内嵌脚本放在CSS的&link&之后。
2 减少外链脚本数量以改善性能
原因很简单,额外的HTTP请求会带来额外的开销,所以减少页面中外链脚本的数量,有助于改善性能。
3 使用无阻塞下载JS方法
无阻塞脚本的秘诀在于,在页面加载完成后才加载JS,即在window对象的load事件触发后在下载脚本。
3.1 使用&script&的defer属性(仅IE和Firefox3.5以上);
defer属性指明本元素所含的脚本不会修改DOM,因此代码能安全的延迟执行。defer属性的&script&,对应的JS文件将在页面解析到&script&时开始下载,但并不会执行,直到DOM加载完成,即onload事件触发前被调用。当一个带有defer属性的JS文件下载时,他不会阻塞浏览器的其它进程,因此这类文件可以与页面中的其他资源并行下载。
&!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&
&html xmlns="http://www.w3.org/1999/xhtml"&
&title&DeferredScripts&/title&
&script type="text/javascript" defer&
alert("defer");
&script type="text/javascript"&
alert("script");
&script type="text/javascript"&
window.onload = function() {
alert("load");
对于支持defer的浏览器弹出顺序是:script&defer&load;而不支持该属性的浏览器的弹出顺序为:defer&script&load。
3.2 使用动态创建的&script&元素来下载并执行代码
实例代码如下:
&!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&
&html xmlns="http://www.w3.org/1999/xhtml"&
&title&DynamicScriptElements&/title&
&script type="text/javascript"&
function loadScript(url, callback) {
var script = document.createElement("Script");
script.type = "text/javascript";
//IE 验证脚本是否下载完成
if (script.readyState) {
script.onreadystatechange = function() {
//readyState属性有5种取值
//uninitialized:初始状态
//loading:开始下载
//interactive:数据完成下载但尚不可用
//complete:数据已经准备就绪
//实际使用时,readyState的值并不像我们预想的那样有规律,实践发现使用readyState
//最靠谱的方式是同时检查以下2个状态,只要其中1个触发,就认为脚本下载完成。
if (script.readyState == "loaded" || script.readyState == "complete") {
//移除事件处理器,确保事件不会处理2次
script.onreadystatechange =
callback();
//其他浏览器
script.onload = function() {
callback();
script.src =
//把新建的&Script&添加到&head&里比添加到&body&里更保险。
document.getElementsByTagName("head")[0].appendChild(script);
//动态加载多个JS文件
//优先加载Common.js,等待Common.js加载完毕后加载Costom.js
//不同浏览器的执行顺序不同
//Firefox、Opera能够保证按照你脚本的加载顺序来执行
//其他浏览器会按照从服务端返回的顺序执行代码,因此使用嵌套的方法保证调用顺序
loadScript("Common.js", function() {
loadScript("Costom.js", function() {
alert("all load");
文件在该元素被添加到页面时开始下载。这种技术的重点在于:无论在何时启动下载,文件的下载与执行不会阻塞页面的其他进程。使用动态脚本节点下载文件时,根据浏览器不同,多数浏览器,返回的代码会立即执行(Firefox、Opera,会等待此前所有动态节点执行完毕)。当脚本”自执行“时,这种机制运行正常,但是当代码内只包含供其它脚本调用的接口时,就必须确保脚本下载完成并准备就绪,在上例中列举了不同浏览器的验证方法。
注意:如果多个文件的顺序很重要,更好的做法是把它们按正确顺序合并为一个文件。此外,说把新建的&Script&添加到&head&里比添加到&body&里更保险是因为要尽量避免页面报错(在低版本的IE中使用不当会发生。
3.3 使用XHR对象下载JS代码并注入页面中
实例代码如下:
&!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&
&html xmlns="http://www.w3.org/1999/xhtml"&
&title&XhrScriptInjection&/title&
&script type="text/javascript"&
var xhr = new XMLHttpRequest();
xhr.open("get", "JScript.js", true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
//2XX表示有效响应,304表示从缓存读取
if (xhr.status &= 200 && xhr.status & 300 || xhr.status == 304) {
//创建内嵌脚本
var script = document.createElement("script");
script.type = "text/javascript";
script.text = xhr.responseT
document.body.appendChild(script); //一旦新创建的&script&被添加到页面,代码就立刻执行然后准备就绪。
xhr.send(null);
这种方法的优点是,你可以下载JS代码但不立即执行。由于代码是在&script&标签之外返回的,因此它下载后不会自动执行,这使得你可以把脚本的执行推迟到你准备好的时候。另一个优点是,同样的代码在所有主流浏览器中都能正常工作。
这种方法的主要局限性是JS文件必须与所有请求的页面处于相同的域。
综上所述,向页面中添加大量JS的推荐做法只需两步:先添加动态加载的所需代码,然后加载初始化页面所需的剩下代码。
&script type="text/javascript" src="Common.js"&&/script&
&script type="text/javascript"&
loadScript("Costom.js", function() {
//Do Something
&title&Operation Aborted Example&/title&
&p&The following code should cause an Operation Aborted error in IE versions prior to 8.&/p&
&script type="text/javascript"&
document.body.appendChild(document.createElement("div"));
上述代码在低版本IE中会报"操作已中止"错误。出现此问题的原因是子容器 HTML 元素包含试图修改父容器元素的子容器的脚本。脚本试图使用 innerHTML 方法或 appendChild 方法修改父容器元素。例如对于如果 DIV 元素是一个 BODY 元素中的子容器,并且在 DIV 元素中的一个 SCRIPT 块试图修改 DIV 元素的父容器的 BODY 元素可能会出现此问题。
最简单的解决方法:将脚本移到body元素的范围。
&title&Operation Aborted Example&/title&
&p&The following code should cause an Operation Aborted error in IE versions prior to 8.&/p&
&script type="text/javascript"&
document.body.appendChild(document.createElement("div"));
&&&&推荐文章:
【上篇】【下篇】»»为什么io.js要从Node.js中分裂出来?
  Node.js中一群不满Joyent公司控制的信徒发起了另外一个项目io.js,即另外一个支持服务器端JavaScript的变种,称为io.js或 。他们为什么要这样做,谁在幕后推动了这一举措,下面是InfoWorld对io.js团队成员Mikeal Rogers的访谈,讨论他们这样做的动机以及他们的期望。
  InfoWorld的:你是这个分支的负责人吗?
  Rogers:还差得远。Fedor Indutny创建了这个分支及其结构。但是这个分支是在一个开放的治理结构-技术委员会的管理之下的(简称TC)。这个团队这周举行了第一次会议,成员有:
Indutny(在Node.js代码的贡献列表上,团队成员)
Trevor Norris(也Node.js的核心团队成员)
Isaac Schlueter(原为Node.js的核心团队校友)
Ben Noordhuis(也是校友)
Bert Belder (另一个校友和Node.js的维护者)
  注* 均为Node社区的重量级人物
  Isaac现为NPM的负责人:
  Ben的故事 一个人称代词引发的论战:
  Rod Vagg(Node.js的支持者)也参与了,他创建并管理构建(build)系统。我只是协调和记录TC会议,并帮助建立议事日程。
  InfoWorld: 你们为什么要创建这个分支?
  Rogers: 7月以,我们一直尝试与Joyent公司一起来转移项目结构,让贡献者和社区可以介入,有效地解决Node.JS面临的[包括缺乏主动和新的贡献者而造成的发布缓慢]的问题。我的猜测是,Fedor厌倦了等待,并开启了io.js。他没有进行任何推广,但我们这些跟他走得很近的人看见了,加入了。虽然我们都了解关于nodejs所有发布工作的核心内容及流程,其中一些人已经在上面工作过一段时间,但不能跟node.js一起发布,由于商标的限制。
  InfoWorld: 你们这些&forkers&是因为不满Joyent对Node.js的控制而创建这个分支的吗?
  Rogers: 我们都认为,顾问委员会的工作正朝着好的方向发展,但是有些事情我们已经等了很久,很多具体的工作一直没有实现。在我看来,将Node向前推动的最好的办法是避开这些问题,并把发布权交给社区,所以这就是我们正在做的。
  InfoWorld: io.js的目标是什么? 我看到有些人像Uber已经在提交代码并使用了。
  Rogers: 嗯,首先最大的目标是发布第一个版本。及时的发布与集成V8的新版本在议程的首位。一路上,在当前项目贡献模型下,我们正在努力吸引更多的人,并确保贡献者能够参与决策及其开放的治理计划。我们也认为这将更容易吸引更多的企业来推动一个纯粹的社区项目,而不是一个由一家公司独有的。至于Uber在用,我并没有注意,但我并不诧异,因为他们一直担心缺少新的发布。
  InfoWorld: 你觉得有与Joyent和ode.js和解的任何机会吗?
  如果Joyent公司决定对Node.js进行开放式的管理,我当然会很高兴,我不认为谁会反对,这是我们都期望看到的结果。但我看不到任何迹像,那些想要解决工作问题的人还只是等着。
  InfoWorld: io.js下一步会做什么?
  Rogers: 在Fedor的生日那天,1月13号发布第一个版本。
  原文地址:
验证消息:Admin10000
提示:更多精彩内容关注微信公众号:全栈开发者中心(admin10000_com)
理智评论文明上网,拒绝恶意谩骂 发表评论 / 共1条评论
登录会员中心JAVA 中IO总结 之前篇阻塞、非阻塞-爱编程
JAVA 中IO总结 之前篇阻塞、非阻塞
最近总结JAVA中的IO,遇到了有关阻塞、非阻塞、同步、异步的概念,之前也做个内核有关开发,今天温故而知新。
Linux支持同步IO,也支持异步IO,因此分为同步阻塞、同步非阻塞,异步阻塞,异步非阻塞。
一、同步阻塞
这是早期Linux常用的IO方式,在这个模型中,用户空间的应用程序执行一个系统调用,这会导致应用程序阻塞。这意味着应用程序会一直阻塞,直到系统调用完成为止(数据传输完成或发生错误)。调用应用程序处于一种不再消费 CPU 而只是简单等待响应的状态,因此从处理的角度来看,这是非常有效的。图
1 给出了传统的阻塞 I/O 模型,这也是目前应用程序中最为常用的一种模型。其行为非常容易理解,其用法对于典型的应用程序来说都非常有效。在调用&read&系统调用时,应用程序会阻塞并对内核进行上下文切换。然后会触发读操作,当响应返回时(从我们正在从中读取的设备中返回),数据就被移动到用户空间的缓冲区中。然后应用程序就会解除阻塞(read&调用返回)。
图1 同步阻塞方式
二、同步非阻塞 I/O
同步阻塞 I/O 的一种效率稍低的变种是同步非阻塞 I/O。在这种模型中,设备是以非阻塞的形式打开的。这意味着 I/O 操作不会立即完成,read操作可能会返回一个错误代码,说明这个命令不能立即满足(EAGAIN&或&EWOULDBLOCK),如图
图2 同步非阻塞方式
非阻塞的实现是 I/O 命令可能并不会立即满足,需要应用程序调用许多次来等待操作完成。这可能效率不高,因为在很多情况下,当内核执行这个命令时,应用程序必须要进行忙碌等待,直到数据可用为止,或者试图执行其他工作。正如图 3 所示的一样,这个方法可以引入 I/O 操作的延时,因为数据在内核中变为可用到用户调用&read&返回数据之间存在一定的间隔,这会导致整体数据吞吐量的降低。
三、异步阻塞方式
另外一个阻塞解决方案是带有阻塞通知的非阻塞 I/O。在这种模型中,配置的是非阻塞 I/O,然后使用阻塞&select&系统调用来确定一个
I/O 描述符何时有操作。使&select&调用非常有趣的是它可以用来为多个描述符提供通知,而不仅仅为一个描述符提供通知。对于每个提示符来说,我们可以请求这个描述符可以写数据、有读数据可用以及是否发生错误的通知。
图3 异步阻塞方式
四、异步非阻塞方式
最后,异步非阻塞 I/O 模型是一种处理与 I/O 重叠进行的模型。读请求会立即返回,说明&read&请求已经成功发起了。在后台完成读操作时,应用程序然后会执行其他处理操作。当&read&的响应到达时,就会产生一个信号或执行一个基于线程的回调函数来完成这次
I/O 处理过程。
图4 异步非阻塞方式
在一个进程中为了执行多个 I/O 请求而对计算操作和 I/O 处理进行重叠处理的能力利用了处理速度与 I/O 速度之间的差异。当一个或多个 I/O 请求挂起时,CPU 可以执行其他任务;或者更为常见的是,在发起其他 I/O 的同时对已经完成的 I/O 进行操作。
五、同步与异步
同步/异步, 它们是消息的通知机制
1. 概念解释
所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。
按照这个定义,其实绝大多数函数都是同步调用(例如sin isdigit等)。
但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务。
最常见的例子就是 SendMessage。
该函数发送一个消息给某个窗口,在对方处理完消息之前,这个函数不返回。
当对方处理完毕以后,该函数才把消息处理函数所返回的值返回给调用者。
异步的概念和同步相对。
当一个异步过程调用发出后,调用者不会立刻得到结果。
实际处理这个调用的部件是在调用发出后,
通过状态、通知来通知调用者,或通过回调函数处理这个调用。
以 Socket为例,
当一个客户端通过调用 Connect函数发出一个连接请求后,调用者线程不用等待结果,可立刻继续向下运行。
当连接真正建立起来以后,socket底层会发送一个消息通知该对象。
C. 三种返回结果途径&
执行部件和调用者可以通过三种途径返回结果:
a.&& 状态、
b. & 通知、
c.&& 回调函数。
可以使用哪一种依赖于执行部件的实现,除非执行部件提供多种选择,否则不受调用者控制。
a. 如果执行部件用状态来通知,
& & 那么调用者就需要每隔一定时间检查一次,效率就很低
& & 有些初学多线程编程的人,总喜欢用一个循环去检查某个变量的值,这其实是一种很严重的错误。
b.&如果是使用通知的方式,
& & 效率则很高,因为执行部件几乎不需要做额外的操作。
c. 至于回调函数,
& & 和通知没太多区别。
2. 举例说明
理解这两个概念,可以用去银行办理业务(可以取钱,也可以存钱)来比喻:
当到银行后,
.可以去ATM机前排队等候 & & & & & & & & & & & & &&& & &-- (排队等候)就是同步等待消息
.可以去大厅拿号,等到排到我的号时,
&柜台的人会通知我轮到我去办理业务. & & & & & &&&-- (等待别人通知)就是异步等待消息.
在异步消息通知机制中,
等待消息者(在这个例子中就是等待办理业务的人)往往注册一个回调机制,
在所等待的事件被触发时由触发机制(在这里是柜台的人)通过某种机制(在这里是写在小纸条上的号码)
找到等待该事件的人.
在select/poll 等IO 多路复用机制中就是fd,
当消息被触发时,触发机制通过fd 找到处理该fd的处理函数.
3. 在实际的程序中,
同步消息通知机制:就好比简单的read/write 操作,它们需要等待这两个操作成功才能返回;
& & & & & & & & & 同步, 是由处理消息者自己去等待消息是否被触发;
异步消息通知机制:类似于select/poll 之类的多路复用IO 操作,
& & & & & & & & & 当所关注的消息被触发时,由消息触发机制通知触发对消息的处理.
& & & & & & & & & 异步, 由触发机制来通知处理消息者;
还是回到上面的例子,
轮到你办理业务, 这个就是你关注的消息,
而办理什么业务, 就是对这个消息的处理,
两者是有区别的.
而在真实的IO 操作时: 所关注的消息就是 & & 该fd是否可读写,
& & & & & & & & & & &而对消息的处理是 & & 对这个fd 进行读写.
同步/异步仅仅关注的是如何通知消息,它们对如何处理消息并不关心,
好比说,银行的人仅仅通知你轮到你办理业务了,
而办理业务什么业务(存钱还是取钱)他们是不知道的.
六、阻塞与非阻塞
阻塞/非阻塞, 它们是程序在等待消息(无所谓同步或者异步)时的状态.
1. 概念解释
阻塞调用是指调用结果返回之前,当前线程会被挂起。函数只有在得到结果之后才会返回。
有人也许会把阻塞调用和同步调用等同起来,实际上他是不同的。
对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回而已。
socket接收数据函数recv是一个阻塞调用的例子。
当socket工作在阻塞模式的时候, 如果没有数据的情况下调用该函数,则当前线程就会被挂起,直到有数据为止。
非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。
C. 对象的阻塞模式和阻塞函数调用
对象是否处于阻塞模式和函数是不是阻塞调用有很强的相关性,但是并不是一一对应的。
阻塞对象上可以有非阻塞的调用方式,我们可以通过一定的API去轮询状态,
在适当的时候调用阻塞函数,就可以避免阻塞。
而对于非阻塞对象,调用特殊的函数也可以进入阻塞调用。函数select就是这样的一个例子。
2. 举例说明
继续上面的那个例子,
不论是排队等待,还是使用号码等待通知,
如果在这个等待的过程中,
. 等待者除了等待消息之外不能做其它的事情,那么该机制就是阻塞的,
& 表现在程序中,也就是该程序一直阻塞在该函数调用处不能继续往下执行.
. 相反,有的人喜欢在银行办理这些业务的时候一边打打电话发发短信一边等待,这样的状态就是非阻塞的,
& 因为他(等待者)没有阻塞在这个消息通知上,而是一边做自己的事情一边等待.
七、易混淆的点
很多人也会把异步和非阻塞混淆,
因为异步操作一般都不会在真正的IO 操作处被阻塞,
比如如果用select 函数,当select 返回可读时再去read 一般都不会被阻塞
就好比当你的号码排到时一般都是在你之前已经没有人了,所以你再去柜台办理业务就不会被阻塞.
可见,同步/异步与阻塞/非阻塞是两组不同的概念,它们可以共存组合,
而很多人之所以把同步和阻塞混淆,我想也是因为没有区分这两个概念,
比如阻塞的read/write 操作中,其实是把消息通知和处理消息结合在了一起,
在这里所关注的消息就是fd 是否可读/写,而处理消息则是对fd 读/写.
当我们将这个fd 设置为非阻塞的时候,read/write 操作就不会在等待消息通知这里阻塞,
如果fd 不可读/写则操作立即返回.
八、同步/异步与阻塞/非阻塞的组合分析
_______阻塞____________________非阻塞_____
同步 | 同步阻塞 & & & & & & &同步非阻塞
异步 | 异步阻塞 & & & & & & &异步非阻塞
同步阻塞形式:
& 效率是最低的,
& 拿上面的例子来说,就是你专心排队,什么别的事都不做。
& 实际程序中
& 就是未对fd 设置O_NONBLOCK 标志位的read/write 操作,
异步阻塞形式:
& 如果在银行等待办理业务的人采用的是异步的方式去等待消息被触发,也就是领了一张小纸条,
& 假如在这段时间里他不能离开银行做其它的事情,那么很显然,这个人被阻塞在了这个等待的操作上面;
& 异步操作是可以被阻塞住的,只不过它不是在处理消息时阻塞,而是在等待消息被触发时被阻塞.
& 比如select 函数,
& 假如传入的最后一个timeout 参数为NULL,那么如果所关注的事件没有一个被触发,
& 程序就会一直阻塞在这个select 调用处.
同步非阻塞形式:
& 实际上是效率低下的,
& 想象一下你一边打着电话一边还需要抬头看到底队伍排到你了没有,
& 如果把打电话和观察排队的位置看成是程序的两个操作的话,
& 这个程序需要在这两种不同的行为之间来回的切换,效率可想而知是低下的;
& 很多人会写阻塞的read/write 操作,
& 但是别忘了可以对fd 设置O_NONBLOCK 标志位,这样就可以将同步操作变成非阻塞的了;
异步非阻塞形式:
& 效率更高,
& 因为打电话是你(等待者)的事情,而通知你则是柜台(消息触发机制)的事情,
& 程序没有在两种不同的操作中来回切换.
& 比如说,这个人突然发觉自己烟瘾犯了,需要出去抽根烟,
& 于是他告诉大堂经理说,排到我这个号码的时候麻烦到外面通知我一下(注册一个回调函数),
& 那么他就没有被阻塞在这个等待的操作上面,自然这个就是异步+非阻塞的方式了.
& 如果使用异步非阻塞的情况,
& 比如aio_*组的操作,当发起一个aio_read 操作时,函数会马上返回不会被阻塞,
& 当所关注的事件被触发时会调用之前注册的回调函数进行处理.
九、epoll模型:
1、select是几乎所有unix、linux都支持的一种多路IO方式,通过select函数发出IO请求后,线程阻塞,一直到数据准备完毕,然后才能把数据从核心空间拷贝到用户空间,所以select是同步阻塞方式。int
select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
2、poll对select的使用方法进行了一些改进,突破了最大文件数的限制,同时使用更加方便一些。
int poll(struct pollfd *ufds, unsigned int nfds, int timeout);
struct pollfd {
& & & & & & & /* 对应的文件描述符 */
& & & & /* 要监听的事件,例如POLLIN|POLLPRI */
& & &/* 返回的事件,用于在poll返回时携带该fd上发生的事情,在poll调用时,该字段会自动被清空 */};
通过poll函数发出IO请求后,线程阻塞,直到数据准备完毕,poll函数在pollfd中通过revents字段返回事件,然后线程把数据从核心空间拷贝到用户空间,所以poll同样是同步阻塞方式,性能同select相比没有改进。
3、epoll是linux为了解决select/poll的性能问题而新搞出来的机制,基本的思路是:由专门的内核线程来不停地扫描fd列表,有结果后,把结果放到fd相关的链表中,用户线程只需要定期从该fd对应的链表中读取事件就可以了。同时,为了节省把数据从核心空间拷贝到用户空间的消耗,采用了mmap的方式,允许程序在用户空间直接访问数据所在的内核空间,不需要把数据copy一份。epoll一共有3个函数:
1.创建epoll文件描述符
int epoll_create(int size);
2.把需要监听的文件fd和事件加入到epoll文件描述符,也可以对已有的fd进行修改和删除
文件fd保存在一个红黑树中,该fd的事件保存在一个链表中(每个fd一个事件链表),事件由内核线程负责填充,用户线程读取
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
& & & & & & typedef union epoll_data {
& & & & & & & & void *
& & & & & & & &
& & & & & & & & __uint32_t u32;
& & & & & & & & __uint64_t u64;
& & & & & & } epoll_data_t;
& & & & & & struct epoll_event {
& & & & & & & & __uint32_ & & &/* Epoll events */
& & & & & & & & epoll_data_ & & &/* User data variable */
& & & & & & };
用户线程定期轮询epoll文件描述符上的事件,事件发生后,读取事件对应的epoll_data,该结构中包含了文件fd和数据地址,由于采用了mmap,程序可以直接读取数据。
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
有人把epoll这种方式叫做同步非阻塞(NIO),因为用户线程需要不停地轮询,自己读取数据,看上去好像只有一个线程在做事情
也有人把这种方式叫做异步非阻塞(AIO),因为毕竟是内核线程负责扫描fd列表,并填充事件链表的
个人认为真正理想的异步非阻塞,应该是内核线程填充事件链表后,主动通知用户线程,或者调用应用程序事先注册的回调函数来处理数据,如果还需要用户线程不停的轮询来获取事件信息,就不是太完美了,所以也有不少人认为epoll是伪AIO,还是有道理的。
版权所有 爱编程 (C) Copyright 2012. . All Rights Reserved.
闽ICP备号-3
微信扫一扫关注爱编程,每天为您推送一篇经典技术文章。}

我要回帖

更多关于 阻塞io和非阻塞io 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信