我以前是写AS3的,事件和回调无处不在,天生就是异步的,很少需要讨论这些概念。现在写C#,这些概念提的就多了,所以需要彻底搞明白。如果把技术的逻辑抽象出来,跟生活中的逻辑做比较,你会发现技术包含的道理是多么的朴素呀。
本程序中的代码大部分是示意代码,不能真正运行。
本程序中的代码大部分是示意代码,不能真正运行。
1、阻塞:阻塞是指当前线程被堵住了,不能继续往下执行了,就被操作系统挂起了。
阻塞的对象是当前线程,而不是IO被阻塞了;外部资源(通常是IO)使得当前线程被挂起才叫阻塞,内部程序执行的再慢也不叫阻塞,比如一个JPEG.Encode(),虽然这个函数的执行很慢,但它仍然在执行,不叫阻塞。阻塞的核心表现是等待,什么事情都做不了,只有等待。
2、非阻塞:调用外部资源,不管结果如何,不会阻挡线程的继续执行。
3、同步:“调用”然后得到“结果”。可能立即得到结果,可能等待一毫秒,也可能等待一辈子,反正结果必须紧随其后。
同步的概念关键在于结果必须紧随调用者之后到来,可以有等待,也可以没有等待,不管有没有等待,同步跟阻塞都没有必然关系。比如同步非阻塞接口Socket.NoBlockRecv(),一旦调用它,有数据就返回数据,没数据就返回空数据或错误,反正不会让你等待。
还有一个同步非阻塞的例子是协程,协程是一种巧妙的机制,既实现了同步返回结果(不像Socket.NoBlockRecv()没有数据就返回空数据或错误当结果),又不会阻塞当前线程。下面是Unity3D的协程的例子:
IEnumerator WaitAndDebug()
{
//打印一句日志
Debug.Log("WaitAndDebug start" + Time.time);
//这句的执行要花费5秒,但5秒时间段内,当前线程可以干其它事,同步非阻塞。
yield return new WaitForSeconds(5);
//5秒后再打印一句日志
Debug.Log("WaitAndDebug done" + Time.time);
}
5、Reactor:响应模式,我是被动的,外部要我干什么,我就干什么,受外部驱动。Reactor模式虽然起源于网络IO设计,但这种思想是通用的。基于Reactor模式的网络IO是这样的:
首先声明要监听哪些事件
socket.addEventHandler("receiveEvent",receiveHandler);
socket.addEventHandler("canSendEvent",canSendHandler);
function receiveHandler()
{
socket.receive();//IO告诉我有数据来了,我才去接收数据,同步的
}
function canSendHandler()
{
socket.send();//IO告诉我它不繁忙了,可以发送数据了,我才去发数据,同步的
}
Reactor的核心是被动响应,程序响应IO事件,具体的收发操作还是需要程序自己去完成,虽然这期间没有阻塞,但收发操作还是同步的。
6、Proactor:主动模式,我是主动的,我想什么时候干,就什么时候干,不受外部驱动。基于Proactor模式的网络IO是这样的:
socket.addEventHandler("receiveSuccessEvent",receiveSuccessHandler);
socket.addEventHandler("sendSuccessEvent",sendSuccessHandler);
//不用关心IO是否繁忙,发就是了,成功了会通知我的,通过sendSuccessEvent事件
socket.sendAsync(data);
//不用关心有没有数据,我想何时接收就接收,如果我不执行接收,就永远不会触发receiveSuccessEvent事件
socket.receiveAsync();
socket.receiveAsync();
function receiveSuccessHandler(data)
{
log("received data");//IO已经把数据接收好了,然后通知我,异步的
}
function sendSuccessHandler()
{
log("send data success");//IO把数据发送成功了,然后通知我,异步的
}
举个形象的例子:去银行取款。
Reactor:拿号,某个柜台空闲了就通知我去取款,我还是必须坐到柜台前取款,取款过程还是同步的。
Proactor:拿号,告诉大堂经理我要取款,款到了,大唐经理送到我手中,取款过程是异步的。
以上都是个人理解,如果有错误的地方,欢迎大家指正。