`

理解异步

 
阅读更多

本文转载自:http://blog.csdn.net/historyasamirror/article/details/6159248

 

所谓同步,简单的说,A告诉B去做某件事情,然后就一直等待,直到B做完后返回给A,A才继续做其它的事情;
所谓异步,相反的,A告诉B做某件事情,然后就去干其它的事情了,B做完后再通知A。

无论是同步还是异步,其实都是指两个对象之间的交互。所以,判断什么是同步还是异步,首先要先明确指的是哪两个对象之间的关系。
举个例子:

 

[java] view plain copy
 
  1. socket.write(request);  
  2. response = socket.read();  

 

这是一段常见的伪代码,用于Client向Server发送请求。在这里,可以说socket.write是一个同步IO,因为调用write操作以后,内核会将要写的数据放入到网卡的缓冲区中,然后再返回,所以,这里同步的两个对象分别是应用程序和内核;也可以说这段代码是一个同步消息机制,因为client将request发送给server后,一直等待直到server将response返回,这里同步的两个对象分别是client和server。

之前的那篇blog专门论述同步IO和异步IO,它指的是应用程序和操作系统内核间的关系。这里就不多谈了。这里谈谈Mina。Mina是一个非常流行的网络程序的框架,它提供的是异步的API(It provides an abstract • event-driven • asynchronous API)。
比如,如果client想要创建一个到server的连接,用mina可以这样写:

 

[java] view plain copy
 
  1.  NioSocketConnector connector = new NioSocketConnector();  
  2. // 初始化connector...  
  3. //..........  
  4. connector.connect(address);  

 

看上去好像和一般的写法没什么两样。但是,这里的connector.connect()方法是一个异步的调用,意思是程序告诉mina要去连接address,mina返回说它会做这件事,但它可能还没有做完。所以,即便“connector.connect(address);”这行代码结束了,也并不意味着连接成功了(mina这时候可能还正在创建连接的过程中)。完整的写法应该是:

 

[java] view plain copy
 
  1. ConnectFuture connFuture = connector.connect(address);  
  2. connFuture.addListener(new ConnectListener());  
  3.         private class ConnectListener implements IoFutureListener<ConnectFuture>{  
  4.               
  5.             public void operationComplete(ConnectFuture future) {  
  6.                   
  7.                 if (future.isConnected()) {  
  8.                     //get session  
  9.                     IoSession session = future.getSession();  
  10.                       
  11.                     session.write(...);  
  12.                       
  13.                 } else {  
  14.                       
  15.                     logger.error("can not create the connection .");  
  16.                 }  
  17.             }  
  18.         }  
  19.     }  
  20. }  

 上面代码是原文我稍微做了下修改

    //future.awaitUninterruptibly();
        future.addListener(new IoFutureListener<ConnectFuture>()
        {
            @Override
            public void operationComplete(ConnectFuture future)
            {
                if(future.isConnected())
                {
                    System.out.println("connected");
                }else {
                    System.out.println("disconnected");
                }
            }
        });
        System.out.println("end");

 这里地址对网络通的情况下,future.awaitUninterruptibly()一行,注释掉则打印 end connected ,不注释掉则打印 connected end 可以稍微注意下

 

这里面最重要的一个类是ConnectListener,它实现了IoFutureListener<ConnectFuture>这个接口。这个类其实只有一个函数 – operationComplete,这是一个回调函数,它告诉mina一旦connect完成以后,就调用这个函数。我们这里的回调函数,首先判断一下连接是否成功,如果成功,那么就向这个链接中写入数据(session.write)。

回调函数在异步机制中扮演着非常重要的角色。 因为在同步机制中,调用者会等到结果返回然后自己执行接下来的操作,比如,上面这段代码如果写成同步的,大概是这个样子:

 

[java] view plain copy
 
  1. boolean status = connector.connect(address);  
  2. if(status) {  
  3.     session.write(...);  
  4. else {  
  5.     logger.error("can not create the connection .");  
  6. }  

 

但是在异步机制中,就只能将connect后面的代码做成回调函数,注册到mina中。这样,当mina完成工作后它才知道接下去该干什么。

值得一提的是,虽然Mina号称是Asynchronous API,但它也提供了同步的方法。比如,上面这段代码,如果用Mina的同步机制是这样写的:

 

[java] view plain copy
 
  1. ConnectFuture future = connector.connect(address);  
  2. future.awaitUninterruptibly();  
  3. IoSession session = future.getSession();  
  4.         // Send the first ping message  
  5.         session.write(....);  

 

重点在于“future.awaitUninterruptibly();”这行代码,它会将程序阻塞住,直到连接创建好,所以,当这行代码结束后,就可以直接获取session并执行write操作。

我在网上找到的大部分Mina示例代码都是基于同步的调用。所以,虽然很多人用mina,可是还是更习惯于同步的机制。至于为什么会这样,以后会再讨论。

 

 

上篇说了半天,却回避了一个重要的问题:为什么要用异步呢,它有什么样的好处?坦率的说,我对这点的认识不是太深刻(套句俗语,只可意会,不可言传)。还是举个例子吧:
比如Client向Server发送一个request,Server收到后需要100ms的处理时间,为了方便起见,我们忽略掉网络的延迟,并且,我们认为Server端的处理能力是无穷大的。在这个use case下,如果采用同步机制,即Client发送request -> 等待结果 -> 继续发送,那么,一个线程一秒钟之内只能够发送10个request,如果希望达到10000 request/s的发送压力,那么Client端就需要创建1000个线程,而这么多线程的context switch就成为client的负担了。而采用异步机制,就不存在这个问题了。Client将request发送出去后,立即发送下一个request,理论上,它能够达到网卡发送数据的极限。当然,同时需要有机制不断的接收来自Server端的response。

以上的例子其实就是这篇的主题,异步的消息机制,基本的流程是这样的:

如果仔细琢磨的话,会发现这个流程中有两个很重要的问题需要解决:
1. 当client接收到response后,怎样确认它到底是之前哪个request的response呢?
2. 如果发送一个request后,这个request对应的response由于种种原因(比如server端出问题了)一直没有返回。client怎么能够发现类似这样长时间没有收到response的request呢?

对于第一个问题,一般会尝试给每个request分配一个独一无二的ID,返回的Response会同时携带这个ID,这样就能够将request和response对应上了。
对于第二个问题,需要有一个timeout机制,对于每一个request都有一个定时器,如果到指定时间仍然没有返回结果,那么会触发timeout操作。多说一句,timeout机制其实对于涉及网络的同步机制也是非常有必要的,因为有可能client与server之间的链接坏了,在极端情况下,client会被一直阻塞住。

分享到:
评论

相关推荐

    Android应用源码之演化理解 异步加载图片.zip

    通过研究源码,开发者可以理解异步加载的核心逻辑,提升处理图片加载问题的能力,也可以借鉴其中的设计模式,用于自己的项目中。 总之,"Android应用源码之演化理解 异步加载图片.zip"是一个宝贵的教育资源,它...

    安卓Android源码——演化理解异步加载图片.zip

    本资料“安卓Android源码——演化理解异步加载图片.zip”着重探讨了Android系统中如何高效地实现这一功能。下面我们将详细解析这个主题,包括异步加载图片的基本原理、历史演进以及常见的解决方案。 首先,让我们...

    安卓Andriod源码——演化理解异步加载图片.zip

    这个名为"安卓Andriod源码——演化理解异步加载图片.zip"的压缩包很可能是为了帮助开发者深入理解这一过程。让我们通过分析标题、描述和提供的文件名"AystnPicture"来探讨异步加载图片的原理及其在Android源码中的...

    Android应用源码之演化理解 异步加载图片.zip项目安卓应用源码下载

    Android应用源码之演化理解 异步加载图片.zip项目安卓应用源码下载Android应用源码之演化理解 异步加载图片.zip项目安卓应用源码下载 1.适合学生毕业设计研究参考 2.适合个人学习研究参考 3.适合公司开发项目技术...

    深入理解Python异步编程1

    首先,我们要理解异步编程的基础概念。异步编程的核心在于处理阻塞和非阻塞的操作。在程序中,阻塞是指当一个操作未完成时,程序会暂停执行,等待这个操作完成。例如,网络I/O、磁盘I/O或用户输入都可能导致阻塞。非...

    安卓Android源码——演化理解 异步加载图片.zip

    总结来说,Android中异步加载图片是一个涉及多层面的技术问题,从基础的线程管理到高级的库使用,再到内存优化和缓存策略,都需要开发者深入理解和掌握。随着Android系统的不断发展,新的工具和技术不断涌现,持续...

    Twisted与异步编程入门

    文章首先强调了理解异步编程模型的重要性,指出只有深入理解模型,才能更好地使用Twisted。 文章通过对比三种编程模型来解释异步编程的概念: 1. **单线程同步模型**:这是最基础的模型,一个时刻只能执行一个任务...

    异步调用流程图

    在IT行业中,异步调用是一种常见的编程模式,特别是在高并发和性能优化的场景下。它使得程序在等待某些操作完成时...对于“绘图7.vsd”这个文件,如果能查看到具体内容,将有助于我们更具体地理解异步调用的实现细节。

    c#异步经典Demo

    C#异步编程是.NET框架中的一个重要特性,它在现代应用程序开发中扮演着核心角色,尤其是在...通过深入研究这些Demo,开发者可以更好地理解异步编程的工作原理,并将其应用于实际项目中,提升应用程序的性能和用户体验。

    C#异步回调

    通过学习`AsyncResult`,开发者可以更好地理解异步编程的本质,这对于过渡到`Task`和`async/await`模式也是有帮助的。 在提供的压缩包文件中,"C#_异步回调测试"可能包含了一个示例项目,演示了如何使用`...

    C#异步TCP聊天程序

    在IT领域,网络编程是构建分布式系统的关键技术之一,而C#语言提供了强大的异步编程支持,使得开发者能够创建高效、响应迅速的TCP...通过实践和理解异步TCP通信,你可以开发出性能优异、用户体验良好的聊天应用程序。

    异步清零和同步清零置数区别

    首先我们来理解异步清零的概念。异步清零是指在计数器或寄存器中,清零信号不受时钟脉冲的影响,只要清零控制端有效,立即就能实现清零功能。异步清零的一个关键特点就是它不依赖于时钟信号的上升沿或下降沿,而是...

    在Web Service中的异步开发模式

    通过分析这个示例,我们可以更深入地理解异步Web服务的工作流程。 五、实际应用场景 1. 大数据处理:当需要处理大量数据或执行复杂计算时,异步模式可以避免阻塞客户端,提高服务的可用性。 2. 文件上传/下载:在...

    异步自实现

    本文将深入探讨基于`IEnumerator`实现的异步代码,尽管它已被更高级的`async`/`await`语法所取代,但这种实现方式对于理解异步编程的底层机制仍然非常有价值。 首先,我们要知道`IEnumerator`接口是.NET中的迭代器...

    几本书的异步电机状态方程比较

    在深入探讨异步电机的状态方程之前,我们首先需要理解异步电机的基本工作原理以及模型构建的重要性。异步电机,又称感应电机,是一种广泛应用于工业和日常生活中的电动机类型。它通过定子绕组产生的旋转磁场与转子绕...

    CVI学习文件-多线程 异步定时器(修改增加学习版)

    2. 异步定时器的使用:理解异步定时器的工作原理,如何设置定时器,以及如何定义和注册回调函数来处理定时事件。 3. 多线程与异步定时器的结合:如何在多线程环境中有效利用异步定时器,例如用定时器触发线程间通信...

    Ansync Test 异步任务带有 详细注释

    4. **事件循环与事件队列**:了解事件循环和事件队列的概念对于理解异步编程至关重要。所有JavaScript代码都在单线程的事件循环中运行,异步操作完成后,其回调函数会被放入事件队列,等待主线程空闲时执行。 5. **...

    wcf服务端异步程序

    通过理解异步编程模型,正确配置服务契约和实现异步操作,可以在不牺牲性能的情况下,构建高效、可扩展的WCF服务。在实际项目中,应根据具体需求选择适合的异步实现方式,并注意权限配置和性能优化。

    C++API异步选择模理

    首先,我们需要理解异步编程的基本概念。在同步编程中,代码执行是线性的,一个任务必须等待前一个任务完成后才能开始。而异步编程则允许任务并发执行,无需等待,从而实现非阻塞操作。在C++中,异步API通常通过回调...

Global site tag (gtag.js) - Google Analytics