同步/异步:
同步
一个任务的完成,如果需要依赖于另一任务时,只有被依赖的任务完成以后,这个依赖的一方才能完成,这个种就叫同步;
一个任务的完成,如果需要依赖于另一任务时,被依赖的任务完成以后,依赖的一方的任务才能完成;例如a依赖b,必须确保b先完成;
这是一种非常可靠的任务序列,两个任务的状态可保存一致;
换一种方式描述同步:
如果一个任务依赖另一任务,这个任务向被依赖任务发起调用请求,这个调用请求不会立即返回,而是要等待对方处理完成才返回,但一旦返回了就一定是处理成功的结果;一旦成功了调用者也就成功了,这叫做同步;
异步
调用者无需等待被调用者的完成,只是通知被依赖的任务要做什么事,依赖的任务自己还会继续执行后面的操作;
换一种方式描述异步:
当调用发出以后,被调用方可以立即返回消息,但返回的并不是最终结果,于是被调用者,可以通过状态通知机制通知调用者;这种机制就叫异步;这种方式就要依赖于通知,回调函数等等;
所谓异步是指不需要等待被依赖的任务完成,只是通知被依赖的任务干什么事,依赖的一方可以继续自己的其它任务,被依赖的任务把最终完成的结果通知依赖方即可;
消息通知机制
同步和异步的消息通知机制不一样
同步要等待对方直接返回消息,所有消息通知机制非常容易;
如果是异步,调用者发出调用以后,直接继续后面的任务,被调用者不能立即返回消息给调用者,所以这个最终的处理结果是靠被调用者处理完以后,事后通过回调函数来通知调用者的;
在异步消息通知中,有3种方式:状态、通知、回调;
使用哪种通知机制,依赖于执行部件本身,在编程时使用的哪种方式;
如果执行部件通过状态来通知,那么,调用者每隔一定时间检查一次,称为轮询;
异步方式中,被调用者将通过通知、消息或回调机制,自己完成以后通知调用者来获取结果;
阻塞/非阻塞:
关注调用者等待调用结果返回之前所处的状态
- 阻塞:block,调用结果返回之前,调用者会被挂起;
- 非阻塞:nonblock,调用结果返回之前,调用者不会被挂起;
一个是站在调用者的角度描述:关注的是当调用者调用另一个任务时,调用者自身接下来要怎么办?
阻塞
是指调用结果返回之前,调用者被挂起(暂停),睡眠状态;一直处于等待消息通知,不能执行其它任务,只有被调用者的结果返回,调用者才被唤醒并继续后面的任务;
一个是站在被调用者的角度描述:关注的是被调用者怎么让调用者知道结果;是同步和异步;
同步阻塞:调用者在被调用者返回结果之前是被挂起的;
同步非阻塞:调用者在等待被调用者返回结果之前没有被挂起,可处理其它任务;
同步和阻塞并不是相同概念
同步调用:很多时候,当前进程可能还处于激活状态,只不过要等待调用者返回,即A调用B,在B完成之前,A不能继续下面的任务;
阻塞:A调用b,在B完成之前,A被挂起了变为睡眠状态;但在同步中,A未必是睡眠的;
非阻塞
对于同步调用,当前线程就是调用者可能并没有被改为睡眠状态,还可以处理其它任务;调用者在等待被调用者返回时,仍然能执行其它消息的处理这种情况就叫非阻塞;
如果当前线程在等待函数返回时,没有执行其它消息处理,而是出于挂起的等待状态这就叫阻塞;即A调用B,B不返回,A就完成不了,所以一定是同步的,只不过在B返回时,A能不能干其它事,如果能就是非阻塞,如果不能就是阻塞;
阻塞和非阻塞是用来描述同步模式下调用者的状态的;阻塞、非阻塞和异步就没有关系了,异步就不可能有阻塞
表面看非阻塞方式能来明显的提高cpu的利用率,但也会带来另开销,线程或是进程要不断的进行切换,处于非阻塞就会带来cpu频繁进行切换导致的开销
两种最常见I/O
对数据流的操作
- 网络IO:本质是socket读写(前导、数据流)
- 磁盘IO:数据流
每次文件IO都会经过两个阶段
以磁盘IO读取为例:
第一阶段:数据首先被内核从socket上或从磁盘上加载至内核的内存空间(缓冲区)中;(较慢)
第二阶段:数据从内核缓冲区复制到用户空间的进程的内存中;(较快)
第一阶段时间:等待数据准备完成;就是从设备加载至内核缓存区;
第二阶段时间:数据从内核复制到进程的阶段;
对于网络IO同磁盘IO不太一样,等待网络上发来的packets,第一阶段是把多个包放在缓冲区中要合并起来,如果IP报文再次分片,还要等待多个片到达合并起来成为一个包,最终把数据品种起来;第二阶段都一样,从内核缓冲区复制到用户空间的进程的内存中;
对于磁盘IO,第一阶段就是从本地磁盘文件读取到内核内存中,第二阶段都一样,从内核缓冲区复制到用户空间的进程的内存中;
五种I/O模型
阻塞 I/O(blocking IO)
在linux中,默认情况下所有的socket都是blocking,同步阻塞IO模型是最简单的IO模型,用户线程在内核进行IO操作时被阻塞
同步阻塞IO模型当用户线程发出IO请求之后,内核会去查看数据是否就绪,如果没有就绪就会等待数据就绪,而用户线程就会处于阻塞状态,用户线程交出CPU。当数据就绪之后,内核会将数据拷贝到用户线程,并返回结果给用户线程,用户线程才解除block状态
非阻塞IO(noblocking I/O)
linux下,可以通过设置socket使其变为non-blocking,noblocking I/O的特点是用户进程需要不断的主动询问kernel数据是否准备好
同步非阻塞IO当用户线程发起一个read操作后,并不需要等待,而是马上就得到了一个结果。如果结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦内核中的数据准备好了,并且又再次收到了用户线程的请求,那么它马上就将数据拷贝到了用户线程,然后返回
I/O 多路复用( IO multiplexing)
IO multiplexing包含select,poll,epoll,select/epoll的好处就在于单个process就可以同时处理多个网络连接的IO。它的基本原理就是select,poll,epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。
I/O 多路复用在多路复用IO模型中,会有一个线程不断去轮询多个socket的状态,只有当socket真正有读写事件时,才真正调用实际的IO读写操作。因为在多路复用IO模型中,只需要使用一个线程就可以管理多个socket,系统不需要建立新的进程或者线程,也不必维护这些线程和进程,并且只有在真正有socket读写事件进行时,才会使用IO资源,所以它大大减少了资源占用
信号驱动IO(signal blocking I/O)
信号驱动IO在信号驱动IO模型中,当用户线程发起一个IO请求操作,会给对应的socket注册一个信号函数,然后用户线程会继续执行,当内核数据就绪时会发送一个信号给用户线程,用户线程接收到信号之后,便在信号函数中调用IO读写操作来进行实际的IO请求操作。
异步 I/O(asynchronous IO)
异步 I/O在异步IO模型中,当用户线程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从内核的角度,当它受到一个asynchronous read之后,它会立刻返回,说明read请求已经成功发起了,因此不会对用户线程产生任何block。然后,内核会等待数据准备完成,然后将数据拷贝到用户线程,当这一切都完成之后,内核会给用户线程发送一个信号,告诉它read操作完成了。也就说用户线程完全不需要关心实际的整个IO操作是如何进行的,只需要先发起一个请求,当接收内核返回的成功信号时表示IO操作已经完成,可以直接去使用数据了。
也就说在异步IO模型中,IO操作的两个阶段都不会阻塞用户线程,这两个阶段都是由内核自动完成,然后发送一个信号告知用户线程操作已完成。用户线程中不需要再次调用IO函数进行具体的读写。这点是和信号驱动模型有所不同的,在信号驱动模型中,当用户线程接收到信号表示数据已经就绪,然后需要用户线程调用IO函数进行实际的读写操作;而在异步IO模型中,收到信号表示IO操作已经完成,不需要再在用户线程中调用iO函数进行实际的读写操作。
网友评论