同步、异步与阻塞、非阻塞
这个几个概念很容易被混淆。这些概念基本都是使用在多个部件进行协作或需要在一定时间内完成的场景中。
首先,将这几个概念的定义和应用场景分析一下:
同步、异步
同步
同步的概念是应用在线程上,线程之间的同步是指两个或多个线程对同一个资源的协调使用。
那么什么是同步函数哪? 仍然与线程和资源有关。这里的资源,可以看做线程的操作任务。通常,此时同步函数内有对资源的同步操作,如mutex等。
当一个线程调用一个同步函数时(例如:该函数用于完成写文件任务),如果该函数没有立即完成规定的操作,则该操作会导致该调用线程的挂起(将CPU的使用权交给系统,让系统分配给其他线程使用),直到该同步函数规定的操作完成才返回,最终才能导致该调用线程被重新调度。
异步
而异步函数,则是即使函数中的操作没有完成,没有结果返回,主线程也不会被阻塞。 而函数的任务则可能由其他线程或子线程来完成。完成后,再通知调用主线程,主线程内应该有相应的机制等待或响应处理结果(这里又是一个主线程对函数结果进行等待处理的过程)。
而当调用结果出来时,通知主线程的方式,即异步调用的实现方式有哪些?
回调函数(register, response)、event(windows)、消息(windows)
阻塞、非阻塞
不同的背景知识,对这些概念的使用范围也不同,这里特指网络IO的阻塞、非阻塞。另外多路IO, 异步IO也是网络IO中的常见形式。
处理IO请求,需要涉及两个对象,一个是本地线程,一个是内核kernel。
一个操作也一般会有两个步骤,如read,首先是数据准备阶段,数据从远程到内核的响应,然后是数据从内核copy到内存阶段。不同的IO类型,这两个阶段的表现形式不同。
若数据准备不足的情况下:
阻塞式IO
linux下,默认socket都是阻塞式IO
数据准备不足情况下,首先,用户线程会被阻塞,kernel会一直等到数据的到来。两个阶段都被阻塞。
非阻塞式IO
若数据准备不足,kernel会返回error给进程。并不会block用户进程,而用户想继续读数据,需要继续询问数据是否准备好了。
用户和kernel都没有被阻塞,但是不能保证获取数据。
而且在数据复制阶段,线程会被阻塞。
多路式IO
单个线程处理多个socket网络连接IO,每个网络连接都是非阻塞式IO,而用户线程是被阻塞,只不过是被select函数阻塞,而不是socket IO阻塞。
异步IO
用户线程发完请求,便立即返回,kernel处理请求,等待数据准备完成,然后,kernel复制数据到内存。
从表象上看,异步的用户进程是没有被阻塞,仍然运行,但kernel中的一个线程会等待“准备数据”(远程数据)。
在数据复制阶段,阻塞、非阻塞、多路IO 的用户线程都会被阻塞,而异步IO不会。
此外,非阻塞与异步的区别,在第一阶段,非阻塞会不断check数据准备是否完成,而异步不会。
以上参考博文:http://blog.csdn.net/historyasamirror/article/details/5778378
http://blog.csdn.net/bzhxuexi/article/details/19552719
回调函数
调用者提供函数指针名称和参数列表,由实现者完成内部的算法或逻辑过程。实现者只需要将这个函数注册一下(与调用者的联系接口),实现者是不负责函数的调用过程,也无法控制函数的调用。
异步调用就是回调函数的一种典型应用。
基于回调函数的异步调用:
A, B两个进程,A向B发送请求,A不等B的响应,继续干自己其他的事情;B收到请求,根据逻辑,在适当的时候处理请求,并调用A的回调函数,完成A的任务。
Dbus
以前用过一个叫DSI的机制,用于分布式系统间的数据传递,能运行与Q-net和Socket之上,是一个master-slave的网络结构。不过这是一个私有的库,资料较少。
对应的开源的机制是D-BUS,只不过dbus是用于桌面应用程序之间的进程通信,或进程与系统内核之间的通信Inter Process communication,IPC. 它也是基于socket实现的。
它的其他特性:低时延、低消耗,二进制的数据交换协议,支持一对一和多对多(需要daemon转发)。
而且,有人做了很漂亮的表格,将dbus与android的IPC通信机制binder进行比较:
比较项 指标 |
DBUS |
Binder |
大数据传输 |
性能一般,因为数据要拷贝 |
性能高,因为共享内存只改变映射,无内存拷贝 |
反映速度 |
好 |
好 |
底层机制 |
Socket |
Share Memory |
组网方式 |
多对多 |
一对一 |
语言支持(仅限Android平台) |
C |
C++ 和JAVA |
第三方参与 |
Dbus-Daemon全程参与 |
Service Manager, 仅参与服务获取 |
通信方式 |
Signal, method call, method return, error messages |
Method return, Link to death |
寻址方式 |
对象路径注册,表格函数指针,如/org/cups/printer… |
在Host进程注册服务,函数解析(JAVA层有工具) |
安全 |
Authentication机制,复杂 |
UID检查,简单,Android采用 |
面向对象 |
否 |
是 |
应用层开发 |
不支持 |
支持 |
代码大小 |
Dbus + glib(简化版) |
已内建 |
以上表格来源:http://blog.sina.com.cn/s/blog_5377f4930100pe01.html
关系
dbus与同步、异步、阻塞、非阻塞这些个概念有什么关系哪?
这里要看看涉及哪些对象?
一个dbus的daemon守护进程,用于数据转发与管理client和服务端。
两个进程A, B; A可以做服务端,B做客户端。
method call消息,method return消息:如一种流程:B会首先向A发出method call的消息, A通过method return的消息将结果发给B。
error消息:调用方法异常时,返回的错误消息
signal消息:类似于“事件”,通知指定的信号发生了。它们不返回任何内容。
--------------------------------
那么A是如何响应method call消息哪? B又是如何响应method return消息哪?
流程:
方法调用的一般流程:
1.使用不同语言绑定的dbus高层接口,都提供了一些代理对象,调用其他进程里面的远端对象就像是在本地进程中的调用一样。应用调用代理上的方法,代理将构造一个方法调用消息给远端的进程。
2.在DBUS的底层接口中,应用需要自己构造方法调用消息(method call message),而不能使用代理。
3.方法调用消息里面的内容有:目的进程的bus name,方法的名字,方法的参数,目的进程的对象路径,以及可选的接口名称。
4.方法调用消息是发送到bus daemon中的。
5.bus daemon查找目标的bus name,如果找到,就把这个方法发送到该进程中,否则,daemon会产生错误消息,作为应答消息给发送进程。
6.目标进程解开消息,在dbus底层接口中,会立即调用方法,然后发送方法的应答消息给daemon。在dbus高层接口中,会先检测对象路径,接口,方法名称,然后把它转换成对应的对象(如GObject,QT中的QObject等)的方法,然后再将应答结果转换成应答消息发给daemon。
7.bus daemon接受到应答消息,将把应答消息直接发给发出调用消息的进程。
8.应答消息中可以包容很多返回值,也可以标识一个错误发生,当使用绑定时,应答消息将转换为代理对象的返回值,或者进入异常。
消息通过连接到达目的端后,连接会将挂起在该连接上的进程唤醒,由该进程将消息取走。每个连接都有一个唯一的名字和可选的其他多个名字,用于在多对多通讯时指明消息的发送者和接收者。
消息是Dbus的IPC机制中的一个信息传递媒介。
调用“方法”的方式有哪些?
1.同步调用,使用dbus_g_proxy_call发送方法请求到远端对象,dbus会阻塞等待远端对象的回应,输出参数里将会带有相应的回应数据,以G_TYPE_INVALID作为终止符。
2.异步调用,使用dbus_g_proxy_begin_call,它将返回一个DBusGPendingCall对象,可以使用dbus_g_pending_call_set_notify连接到自己的处理函授中。
有一点确定的是,各目标进程会响应消息,调用指定的方法。
对于同步调用的方式,为防止长时间阻塞另外一个进程,尽量快速处理。
-------
work thread 是与daemon守护进程直接通信的底层的工作进程,当多个请求转向同一client端时,就会启动多个work thread,每个thread相互独立,所以,他们之间不会阻塞。消息响应的次序也不是由请求顺序决定的。
参考:http://blog.csdn.net/linweig/article/details/5068136
来源:CSDN
作者:viewcode
链接:https://blog.csdn.net/viewcode/article/details/21161311