异步调用和非阻塞调用有什么区别?还在阻塞和同步调用之间(请提供示例)?
异步调用和非阻塞调用有什么区别?还在阻塞和同步调用之间(请提供示例)?
Answers:
在许多情况下,它们是同一事物的不同名称,但是在某些情况下,它们是完全不同的。因此,这取决于。术语并不是在整个软件行业中以完全一致的方式应用。
例如,在经典的套接字API中,非阻塞套接字是一个简单地立即返回特殊“错误块”错误消息的套接字,而阻塞套接字会被阻塞。您必须使用单独的函数,例如select
或,poll
以找出何时是重试的好时机。
但是,异步套接字(Windows套接字支持)或.NET中使用的异步IO模式更为方便。您调用一个方法来开始操作,框架完成后会回调您。即使在这里,也存在基本差异。异步Win32套接字通过传递Window消息将其结果“编组”到特定的GUI线程,而.NET异步IO是自由线程的(您不知道将在哪个线程上调用回调)。
因此,它们并不总是意味着同一件事。为了说明套接字示例,我们可以说:
同步/异步是描述两个模块之间的关系。
阻塞/非阻塞是描述一个模块的情况。
例如:
模块X:“ I”。
模块Y:“书店”。
X问Y:您有一本书名为“ c ++入门”吗?
1)阻止:Y回答X之前,X一直在那里等待答案。现在X(一个模块)正在阻塞。X和Y是两个线程或两个进程还是一个线程或一个进程?我们不知道。
2)非阻塞:在Y回答X之前,X只是离开那里做其他事情。X可能每两分钟回来一次,以检查Y是否完成工作?还是X在Y打电话给他之前不会回来?我们不知道 我们只知道X在Y完成工作之前可以做其他事情。X(一个模块)是非阻塞的。X和Y是两个线程还是两个进程或一个进程?我们不知道。但是我们确定X和Y不能是一个线程。
3)同步:在Y回答X之前,X一直在那里等待答案。这意味着X直到Y完成其工作才能继续。现在我们说:X和Y(两个模块)是同步的。X和Y是两个线程或两个进程还是一个线程或一个进程?我们不知道。
4)异步:在Y回答X之前,X离开那里,X可以做其他工作。X不会回来,直到Y打电话给他。现在我们说:X和Y(两个模块)是异步的。X和Y是两个线程还是两个进程或一个进程?我们不知道。但是我们确定X和Y不能是一个线程。
请注意上面的两个大胆的句子。为什么2)中的粗体句子包含两种情况,而4)中的粗体句子仅包含一种情况?这是非阻塞和异步之间区别的关键。
这是有关非阻塞和同步的典型示例:
// thread X
while (true)
{
msg = recv(Y, NON_BLOCKING_FLAG);
if (msg is not empty)
{
break;
}
sleep(2000); // 2 sec
}
// thread Y
// prepare the book for X
send(X, book);
您可以看到这种设计是非阻塞的(可以说,大多数情况下,该循环都在做些废话,但在CPU看来,X正在运行,这意味着X在无阻塞的情况下),而X和Y是同步的,因为X可以在从Y获得书之前,不要继续做任何其他事情(X不能跳出循环)。
通常,在这种情况下,使X阻塞要好得多,因为非阻塞会在愚蠢的循环上花费大量资源。但是此示例可以帮助您理解以下事实:非阻塞并不意味着异步。
这四个词的确使我们很容易混淆,我们应该记住的是,这四个词用于建筑设计。了解如何设计好的体系结构是区分它们的唯一方法。
例如,我们可以设计这种架构:
// Module X = Module X1 + Module X2
// Module X1
while (true)
{
msg = recv(many_other_modules, NON_BLOCKING_FLAG);
if (msg is not null)
{
if (msg == "done")
{
break;
}
// create a thread to process msg
}
sleep(2000); // 2 sec
}
// Module X2
broadcast("I got the book from Y");
// Module Y
// prepare the book for X
send(X, book);
在这里的例子中,我们可以说
如果需要,还可以用四个词描述在X1中创建的那些线程。
更重要的是:什么时候使用同步而不是异步?我们什么时候使用阻塞而不是非阻塞?
为什么Nginx是非阻塞的?为什么要阻止Apache?
为了做出好的选择,您必须分析您的需求并测试不同体系结构的性能。没有适合各种需求的架构。
将这个问题放在Java 7中的NIO和NIO.2上下文中,异步IO比非阻塞要先进得多。使用Java NIO非阻塞调用,可以通过调用来设置所有通道(SocketChannel,ServerSocketChannel,FileChannel等)AbstractSelectableChannel.configureBlocking(false)
。但是,在这些IO调用返回之后,您可能仍然需要控制检查,例如是否以及何时再次读取/写入等
。例如,
while (!isDataEnough()) {
socketchannel.read(inputBuffer);
// do something else and then read again
}
使用Java 7中的异步api,可以以更加通用的方式来制作这些控件。两种方法之一是使用CompletionHandler
。请注意,这两个read
调用都是非阻塞的。
asyncsocket.read(inputBuffer, 60, TimeUnit.SECONDS /* 60 secs for timeout */,
new CompletionHandler<Integer, Object>() {
public void completed(Integer result, Object attachment) {...}
public void failed(Throwable e, Object attachment) {...}
}
}
FileChannel
是不可选择的,并且不能配置为非阻止。
正如您可能从众多不同的答案(通常是互斥的)中看到的那样,这取决于您问的是谁。在某些领域,这些术语是同义词。或者它们可能各自引用两个相似的概念:
在这两种情况下,其目的都是不允许程序被阻塞,以等待缓慢的过程完成-期望程序如何响应是唯一的实际差异。哪个术语指的是哪个也从程序员到程序员,从语言到语言或从平台到平台而变化。或者这些术语可能指的是完全不同的概念(例如与线程编程有关的同步/异步使用)。
抱歉,但我不认为有一个正确的答案在全球范围内都是正确的。
一个非阻塞调用立即返回所有数据资料:字节的完整号码请求,较少或根本没有。
一个异步调用请求,将在其全部(整体)进行,但将在未来某个时间完成转移。
同步被定义为同时发生。
异步被定义为不在同一时间发生。
这是造成第一个混乱的原因。同步实际上就是所谓的并行。当异步是顺序的时,请执行此操作,然后执行该操作。
现在,整个问题都涉及到对异步行为进行建模,因为您已经进行了一些操作,而该操作需要其他操作的响应才能开始。因此,这是一个协调问题,您如何知道现在可以开始该操作?
最简单的解决方案称为阻塞。
阻塞是指您仅选择等待另一件事完成并返回响应,然后再继续进行需要的操作。
因此,如果您需要在烤面包上放黄油,那么您首先需要烤制烤面包。协调他们的方式是,您首先烤掉繁殖的面包,然后不断凝视烤面包机,直到烤面包机爆裂,然后再在上面放黄油。
这是最简单的解决方案,并且效果很好。没有真正的理由不使用它,除非您碰巧还需要做其他不需要与操作协调的事情。例如,洗碗。当您知道这会花费一些时间并且可以在菜盘洗完时将其洗净时,为什么还要不停地凝视着烤面包机,以吐出吐司呢?
这就是另外两个分别称为非阻塞和异步的解决方案发挥作用的地方。
非阻塞是指您在等待操作完成时选择执行其他不相关的操作。如果您认为合适,请检查响应的可用性。
因此,与其看着烤面包机使其弹出,不如将其弹出。你去洗一整盘。然后,您可以窥视烤面包机,看看是否已经吐出了烤面包。如果没有,则您去洗另一盘,检查一下各盘之间的烤面包机。当您看到烤面包突然冒出时,您就停止洗碗了,取而代之的是烤面包并继续往上面放黄油。
但是,不得不不断检查烤面包会很烦人,想像一下烤面包机在另一个房间里。在两道菜之间,您浪费时间去另一个房间检查吐司。
这是异步的。
异步是指您在等待操作完成时选择执行其他不相关的事情。但是,您无需进行检查,而是将检查工作委派给其他事情,可以是操作本身或观察者,并且可以在响应可用时通知并可能干扰您,以便您可以进行其他操作:需要它。
这是一个奇怪的术语。这没有什么意义,因为所有这些解决方案都是创建相关任务异步协调的方法。这就是为什么我更喜欢将其称为事件。
因此,对于这一点,您决定升级烤面包机,以便在烤面包完成后发出蜂鸣声。即使您正在洗碗,您也经常在听。听到哔哔声后,您便在记忆中排队,一旦您洗完当前碗碟,便会停下来,将黄油放在烤面包上。或者,您可以选择中断当前菜的清洗,并立即处理烤面包。
如果您听不到蜂鸣声,可以让您的伴侣为您看烤面包机,并在烤面包准备好时告诉您。您的伙伴可以自己选择以上三种策略中的任何一种来协调其观看烤面包机并告诉您准备就绪的任务。
最后要说的是,很好的理解是,尽管非阻塞和异步(或我更喜欢称其为事件的)确实可以让您在等待时做其他事情,但您却没有。您可以选择不停地循环检查非阻塞呼叫的状态,而无需执行其他任何操作。但是,这通常比阻止更糟(例如,先看烤面包机,然后移开,然后再回到它,直到完成),因此许多非阻止API允许您从其过渡到阻止模式。对于事件,您可以等待空闲直到收到通知。在这种情况下,不利的一面是添加通知很复杂,而且一开始的成本很高。您必须购买具有蜂鸣功能的新烤面包机,或者说服您的合作伙伴为您服务。
还有一件事,您需要意识到这三个方面所提供的权衡。一个显然不比其他更好。想想我的例子。如果您的烤面包机是如此之快,您将没有时间洗碗,甚至没有开始洗碗,这就是您的烤面包机的速度。在这种情况下,开始其他事情只是浪费时间和精力。阻止就可以了。同样,如果洗碗需要的时间比烘烤的时间长10倍。您必须问自己,完成工作更重要的是什么?到那时,吐司可能会变得又冷又硬,这不值得,阻塞也可以。或者,您应该在等待时选择更快的操作。显然还有很多,但是我的答案已经很长了,我的意思是您需要考虑所有这些,以及实现每个对象的复杂性,以确定它们是否值得,以及是否值得。
编辑:
即使已经很长,我也希望它完整,所以我还要再加两点。
1)通常还存在一种称为多路复用的第四种模型。在这种情况下,当您等待一个任务时,又启动了另一个任务,而当您同时等待两个任务时,又启动了一个任务,依此类推,直到所有任务都开始运行,然后您才等待空闲,但是在所有他们。因此,一旦完成任何操作,您就可以继续处理其响应,然后再返回等待其他响应。这被称为多路复用,因为在等待时,您需要一个接一个地检查每个任务,以查看它们是否已经完成,直到再次完成为止。这是对普通非阻塞功能的扩展。
在我们的示例中,这就像启动烤面包机,然后是洗碗机,然后是微波炉,等等。然后等待其中的任何一个。如果要检查烤面包机是否完成,如果没有,请检查洗碗机,如果没有,请检查微波炉,然后再次检查。
2)尽管我认为这是一个大错误,但同步通常经常一次表示一件事。并且一次异步许多事情。因此,您将看到同步阻塞和非阻塞用于表示阻塞和非阻塞。异步阻塞和非阻塞用于表示复用和事件。
我不太了解我们如何到达那里。但是当涉及到IO和计算时,同步和异步通常指的是所谓的非重叠和重叠。也就是说,异步意味着IO和计算是重叠的,也就是同时发生。而同步意味着它们不是,因此顺序发生。对于同步非阻塞,这意味着您不启动其他IO或计算,而只是忙于等待并模拟阻塞调用。我希望人们不再像那样滥用同步和异步。因此,我不鼓励这样做。
阻止呼叫:仅当呼叫完成时,控件才返回。
非阻塞呼叫:控制立即返回。以后的OS以某种方式通知进程该呼叫已完成。
同步程序:使用阻塞调用的程序。为了避免在调用过程中冻结,它必须具有2个或更多线程(这就是为什么它被称为“同步-线程正在同步运行”的原因)。
异步程序:使用非阻塞调用的程序。它只能有1个线程,并且仍然保持交互。