﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-Error-文章分类-高性能话题</title><link>http://www.cppblog.com/Error/category/20192.html</link><description /><language>zh-cn</language><lastBuildDate>Thu, 08 Nov 2012 12:51:26 GMT</lastBuildDate><pubDate>Thu, 08 Nov 2012 12:51:26 GMT</pubDate><ttl>60</ttl><item><title>(转) 高性能IO设计的Reactor和Proactor模式 </title><link>http://www.cppblog.com/Error/articles/194883.html</link><dc:creator>Enic</dc:creator><author>Enic</author><pubDate>Thu, 08 Nov 2012 09:06:00 GMT</pubDate><guid>http://www.cppblog.com/Error/articles/194883.html</guid><wfw:comment>http://www.cppblog.com/Error/comments/194883.html</wfw:comment><comments>http://www.cppblog.com/Error/articles/194883.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Error/comments/commentRss/194883.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Error/services/trackbacks/194883.html</trackback:ping><description><![CDATA[<p style="margin-top: 0pt; margin-bottom: 0pt" class="p0"><span style="font-size: 15pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;在高性能的<span>I/O</span><span>设计中，有两个比较著名的模式</span><span>Reactor</span><span>和</span><span>Proactor</span><span>模式，其中</span><span>Reactor</span><span>模式用于同步</span><span>I/O</span><span>，而</span><span>Proactor</span><span>运用于异步</span><span>I/O</span><span>操作。</span></span></p>
<p style="margin-top: 0pt; margin-bottom: 0pt" class="p0"><span style="font-size: 15pt"></span></p>
<p style="margin-top: 0pt; margin-bottom: 0pt" class="p0"><span style="font-size: 15pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;在比较这两个模式之前，我们首先的搞明白几个概念，什么是阻塞和非阻塞，什么是同步和异步</span><span style="font-size: 15pt">,</span><span style="font-size: 15pt">同步和异步</span><span style="font-size: 15pt">是针对应用程序和内核的交互而言的</span><span style="font-size: 15pt">，同步指的是用户进程触发<span>IO</span><span>操作并等待或者轮询的去查看</span><span>IO</span><span>操作是否就绪，而异步是指用户进程触发</span><span>IO</span><span>操作以后便开始做自己的事情，而当</span><span>IO</span><span>操作已经完成的时候会得到</span><span>IO</span><span>完成的通知。而阻塞和非阻塞是针对于进程在访问数据的时候，根据</span><span>IO</span><span>操作的就绪状态来采取的不同方式，说白了是一种读取或者写入操作函数的实现方式，阻塞方式下读取或者写入函数将一直等待，而非阻塞方式下，读取或者写入函数会</span></span><span style="font-size: 15pt">立即</span><span style="font-size: 15pt">返回一个状态值。</span><span style="font-size: 15pt"></span></p>
<p style="margin-top: 0pt; text-indent: 21pt; margin-bottom: 0pt" class="p0"><span style="font-size: 15pt">&nbsp;&nbsp;</span><span style="font-size: 15pt">一般来说<span>I/O</span><span>模型可以分为：同步阻塞，同步非阻塞，异步阻塞，异步非阻塞</span><span>IO</span></span></p>
<p style="margin-top: 0pt; text-indent: 21pt; margin-bottom: 0pt" class="p0"><span style="font-size: 15pt"></span><span style="font-size: 15pt"></span></p>
<p style="margin-top: 0pt; margin-bottom: 0pt" class="p0"><span style="font-size: 15pt">&nbsp;&nbsp;&nbsp;</span><span style="font-style: italic; font-size: 15pt; font-weight: bold">同步阻塞<span>IO</span><span>：</span></span><span style="font-style: italic; font-size: 15pt; font-weight: bold"></span></p>
<p style="margin-top: 0pt; margin-bottom: 0pt" class="p0"><span style="font-size: 15pt">&nbsp;&nbsp;&nbsp;在此种方式下，用户进程在发起一个<span>IO</span><span>操作以后，必须等待</span><span>IO</span><span>操作的完成，只有当真正完成了</span><span>IO</span><span>操作以后，用户进程才能运行。</span></span><span style="font-size: 15pt">JAVA<span>传统的</span><span>IO</span><span>模型属于此种方式！</span></span></p>
<p style="margin-top: 0pt; margin-bottom: 0pt" class="p0"><span style="font-size: 15pt"></span></p>
<p style="margin-top: 0pt; margin-bottom: 0pt" class="p0"><span style="font-size: 15pt">&nbsp;&nbsp;&nbsp;</span><span style="font-style: italic; font-size: 15pt; font-weight: bold">同步非阻塞<span>IO:</span></span><span style="font-style: italic; font-size: 15pt; font-weight: bold"></span></p>
<p style="margin-top: 0pt; margin-bottom: 0pt" class="p0"><span style="font-size: 15pt">在此种方式下，用户进程发起一个<span>IO</span><span>操作以后</span></span><span style="font-size: 15pt">边可</span><span style="font-size: 15pt">返回做其它事情，但是用户进程需要时不时的询问<span>IO</span><span>操作是否就绪，这就要求用户进程不停的去询问，从而引入不必要的</span><span>CPU</span><span>资源浪费。其中目前</span><span>JAVA</span><span>的</span><span>NIO</span><span>就属于同步非阻塞</span><span>IO</span><span>。</span></span></p>
<p style="margin-top: 0pt; margin-bottom: 0pt" class="p0"><span style="font-size: 15pt"></span></p>
<p style="margin-top: 0pt; margin-bottom: 0pt" class="p0"><span style="font-size: 15pt"></span></p>
<p style="margin-top: 0pt; margin-bottom: 0pt" class="p0"><span style="font-size: 15pt">&nbsp;&nbsp;&nbsp;</span><span style="font-style: italic; font-size: 15pt; font-weight: bold">异步阻塞<span>IO</span><span>：</span></span><span style="font-style: italic; font-size: 15pt; font-weight: bold"></span></p>
<p style="margin-top: 0pt; margin-bottom: 0pt" class="p0"><span style="font-size: 15pt">&nbsp;&nbsp;&nbsp;此种方式下是指应用发起一个<span>IO</span><span>操作以后，不等待内核</span><span>IO</span><span>操作的完成，等内核完成</span><span>IO</span><span>操作以后会通知应用程序，这其实就是同步和异步最关键的区别，同步必须等待或者主动的去询问</span><span>IO</span><span>是否完成，那么为什么说是阻塞的呢？因为此时是通过</span><span>select</span><span>系统调用来完成的，而</span><span>select</span><span>函数本身的实现方式是阻塞的，而采用</span><span>select</span><span>函数有个好处就是它可以同时监听多个文件句柄，从而提高系统的并发性！</span></span></p>
<p style="margin-top: 0pt; margin-bottom: 0pt" class="p0"><span style="font-size: 15pt"></span></p>
<p style="margin-top: 0pt; margin-bottom: 0pt" class="p0"><span style="font-size: 15pt"></span></p>
<p style="margin-top: 0pt; margin-bottom: 0pt" class="p0"><span style="font-size: 15pt">&nbsp;&nbsp;&nbsp;</span><span style="font-style: italic; font-size: 15pt; font-weight: bold">异步非阻塞<span>IO:</span></span><span style="font-style: italic; font-size: 15pt; font-weight: bold"></span></p>
<p style="margin-top: 0pt; margin-bottom: 0pt" class="p0"><span style="font-size: 15pt">&nbsp;&nbsp;&nbsp;在此种模式下，用户进程只需要发起一个<span>IO</span><span>操作然后立即返回，等</span><span>IO</span><span>操作真正的完成以后，应用程序会得到</span><span>IO</span><span>操作完成的通知，此时用户进程只需要对数据进行处理就好了，不需要进行实际的</span><span>IO</span><span>读写操作，因为</span></span><span style="font-size: 15pt">真正的</span><span style="font-size: 15pt">IO<span>读取或者写入操作已经由</span></span><span style="font-size: 15pt">内核完成了。目前<span>Java</span><span>中还没有支持此种</span><span>IO</span><span>模型。&nbsp;&nbsp;&nbsp;</span></span><span style="font-size: 15pt"></span></p>
<p style="margin-top: 0pt; margin-bottom: 0pt" class="p0"><span style="font-size: 15pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 搞清楚了以上概念以后，我们再回过头来看看，<span>Reactor</span><span>模式和</span><span>Proactor</span><span>模式。</span></span><span style="font-size: 15pt"></span></p>
<p style="margin-top: 0pt; text-indent: 21pt; margin-bottom: 0pt" class="p0"><span style="font-size: 15pt">首先来看看<span>Reactor</span><span>模式，</span><span>Reactor</span><span>模式应用于同步</span><span>I/O</span><span>的场景。我们分别以读操作和写操作为例来看看</span><span>Reactor</span><span>中的具体步骤：</span></span><span style="font-size: 15pt"></span></p>
<p style="margin-top: 0pt; text-indent: 21pt; margin-bottom: 0pt" class="p0"><span style="font-size: 15pt; font-weight: bold">读取操作</span><span style="font-size: 15pt">：</span><span style="font-size: 15pt"></span></p>
<p class="p15"><span style="font-size: 15pt">1.&nbsp;</span><span style="font-size: 15pt">应用程序注册读就需事件和相关联的事件处理器</span><span style="font-size: 15pt"></span></p>
<p class="p15"><span style="font-size: 15pt">2.&nbsp;</span><span style="font-size: 15pt">事件分离器等待事件的发生</span><span style="font-size: 15pt"></span></p>
<p class="p15"><span style="font-size: 15pt">3.&nbsp;</span><span style="font-size: 15pt">当发生读就需事件的时候，事件分离器调用第一步注册的事件处理器</span><span style="font-size: 15pt"></span></p>
<p class="p15"><span style="font-size: 15pt">4.&nbsp;</span><span style="font-size: 15pt">事件处理器首先执行实际的读取操作，然后根据读取到的内容进行进一步的处理</span><span style="font-size: 15pt"></span></p>
<p style="margin-top: 0pt; text-indent: 21pt; margin-bottom: 0pt" class="p0"><span style="font-size: 15pt">写入操作类似于读取操作，只不过第一步注册的是写</span><span style="font-size: 15pt">就绪</span><span style="font-size: 15pt">事件。</span><span style="font-size: 15pt"></span></p>
<p style="margin-top: 0pt; text-indent: 21pt; margin-bottom: 0pt" class="p0"><span style="font-size: 15pt">下面我们来看看<span>Proactor</span><span>模式中读取操作和写入操作的过程：</span></span><span style="font-size: 15pt"></span></p>
<p style="margin-top: 0pt; text-indent: 21pt; margin-bottom: 0pt" class="p0"><span style="font-size: 15pt; font-weight: bold">读取操作：</span><span style="font-size: 15pt; font-weight: bold"></span></p>
<p class="p15"><span style="font-size: 15pt">1.&nbsp;</span><span style="font-size: 15pt">应用程序初始化一个异步读取操作，然后注册相应的事件处理器，此时事件处理器不关注读取</span><span style="font-size: 15pt">就绪</span><span style="font-size: 15pt">事件，而是关注读取完成事件，这是区别于<span>Reactor</span><span>的关键。</span></span><span style="font-size: 15pt"></span></p>
<p class="p15"><span style="font-size: 15pt">2.&nbsp;</span><span style="font-size: 15pt">事件分离器等待读取操作完成事件</span><span style="font-size: 15pt"></span></p>
<p class="p15"><span style="font-size: 15pt">3.&nbsp;</span><span style="font-size: 15pt">在事件分离器等待读取操作完成的时候，操作系统调用内核线程完成读取操作，并将读取的内容放入用户传递过来的缓存区中。这也是区别于<span>Reactor</span><span>的一点，</span><span>Proactor</span><span>中，应用程序需要传递缓存区。</span></span><span style="font-size: 15pt"></span></p>
<p class="p15"><span style="font-size: 15pt">4.&nbsp;</span><span style="font-size: 15pt">事件分离器捕获到读取完成事件后，激活应用程序注册的事件处理器，事件处理器直接从缓存区读取数据，而不需要进行实际的读取操作。</span><span style="font-size: 15pt"></span></p>
<p style="margin-top: 0pt; margin-bottom: 0pt; margin-left: 18pt" class="p15"><span style="font-size: 15pt">Proactor<span>中写入操作和读取操作，只不过感兴趣的事件是写入完成事件。</span></span><span style="font-size: 15pt"></span></p>
<p style="margin-top: 0pt; text-indent: 41.6pt; margin-bottom: 0pt; margin-left: 18pt" class="p15"><span style="font-size: 15pt">从上面可以看出，<span>Reactor</span><span>和</span><span>Proactor</span><span>模式的主要区别就是真正的读取和写入操作是有谁来完成的，</span><span>Reactor</span><span>中需要应用程序自己读取或者写入数据，而</span><span>Proactor</span><span>模式中，应用程序不需要进行实际的读写过程，它只需要从缓存区读取或者写入即可，操作系统会读取缓存区或者写入缓存区到真正的</span><span>IO</span><span>设备</span><span>.</span></span></p>
<p style="margin-top: 0pt; text-indent: 41.6pt; margin-bottom: 0pt; margin-left: 18pt" class="p15"><span style="font-size: 15pt"></span><span style="font-size: 15pt"></span></p>
<p style="margin-top: 0pt; margin-bottom: 0pt" class="p15"><span style="font-size: 15pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 综上所述，同步和异步是相对于应用和内核的交互方式而言的，同步 需要主动去询问，而异步的时候内核在<span>IO</span><span>事件发生的时候通知应用程序，而阻塞和非阻塞仅仅是系统在调用系统调用的时候函数的实现方式而已。</span></span><span style="font-size: 15pt"></span></p> <img src ="http://www.cppblog.com/Error/aggbug/194883.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Error/" target="_blank">Enic</a> 2012-11-08 17:06 <a href="http://www.cppblog.com/Error/articles/194883.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>