异步与非阻塞

use*_*561 352 asynchronous synchronous blocking

异步和非阻塞调用之间有什么区别?阻塞和同步调用之间(请举例)?

Dan*_*ker 284

在许多情况下,它们是同一个东西的不同名称,但在某些情况下它们是完全不同的.所以这取决于.术语不是以完全一致的方式应用于整个软件行业.

例如,在经典套接字API中,非阻塞套接字只是立即返回一个特殊的"会阻塞"错误消息,而阻塞套接字则会被阻塞.您必须使用单独的功能,例如selectpoll找出何时是重试的好时机.

但异步套接字(由Windows套接字支持)或.NET中使用的异步IO模式更方便.您调用一个方法来启动一个操作,框架会在完成后回调您.即使在这里,也存在基本的差异.异步Win32套接字通过传递Window消息将其结果"编组"到特定的GUI线程,而.NET异步IO是自由线程的(您不知道将调用哪个线程回调).

所以他们并不总是意味着同样的事情.为了提炼插座示例,我们可以说:

  • 阻塞和同步意味着同样的事情:你调用API,它挂起线程,直到它有某种答案并返回给你.
  • 非阻塞意味着如果无法快速返回答案,则API会立即返回错误并且不执行任何操作.因此必须有一些相关的方法来查询API是否可以被调用(即,以有效的方式模拟等待,以避免在紧密循环中手动轮询).
  • 异步意味着API总是立即返回,已经开始"后台"努力来完成您的请求,因此必须有一些相关的方法来获得结果.

  • 而不是说非阻塞调用返回"错误",我认为更准确地说非阻塞调用尽可能多地立即完成,然后表明它做了多少.对于某些操作,完成的工作量将是"一切"或"没有",但是一些其他操作(如流I/O)可能返回定量指示.非阻塞在语义上等同于阻塞具有非常短的超时*如果*阻塞I/O实现将允许稍后将平滑地重试的操作(一些操作;一些不执行). (5认同)
  • 感谢您指出这些术语是上下文敏感的,有时可能会使用不一致.我特别在技术方面,但在其他方面也发现,承认这一事实通常比争论哪些精确定义是正确的更有用,有时会发生. (4认同)
  • 后续问:答案似乎在两个术语之间做出了两种不同的区分.首先,通知:非阻塞意味着应用程序必须稍后再次检查(轮询),而异步意味着我们可以忘记它并依赖框架/ OS通过回调或发布事件来通知我们.其次,动作:非阻塞绝对不会产生任何错误,而异步会使动作排队或在某种意义上"在后台"执行.在区分条款方面哪个区别更重要?这两种区别与一个术语的关系是否更强烈?还是模棱两可? (2认同)
  • @ChadNB-术语,非阻塞与轮询紧密相关。关于API是否“记住”您的调用尝试,API记住的唯一原因是为了给您回叫。如果要调用它进行重新轮询,那么您已经必须维护必要的状态才能知道进行该后续调用,因此API也会通过维护状态而没有任何价值。 (2认同)

Nik*_*sov 46

  • 异步是指并行完成的事情,比如另一个线程.
  • 非阻塞通常是指轮询,即检查给定条件是否成立(套接字是否可读,设备是否有更多数据等)

  • 对于读到这个答案的任何人:这不是在争论准确的措辞.同样,并发和并行不是相同的概念,区分它们不是措辞问题.异步性和并行性是两种不同的动物,这种答案不准确地使它们变得相同. (15认同)
  • 当涉及I/O时,异步通常不是"并行",也不是"另一个线程",大多数是基于通知的.那就是:不要阻止,不要轮询,只是获取信号.当然,可以说这个信号来自"现实世界",可以被认为是"另一个线索"...... (14认同)
  • 异步并不一定意味着它是并行完成的,请参见这篇很棒的文章[/sf/ask/132859541/)关于并发和并行编程的stackoverflow。 (2认同)

Yve*_*ves 43

同步/异步是描述两个模块之间的关系.
阻塞/非阻塞是描述一个模块的情况.

例如:
模块X:"我".
模块Y:"书店".
X问Y:你有一本名为"c ++ primer"的书吗?

1)阻塞:在Y回答X之前,X一直在那里等待答案.现在X(一个模块)正在阻塞.X和Y是两个线程还是两个进程或一个线程或一个进程?我们不知道.

2)非阻塞:在Y回答X之前,X只是离开那里并做其他事情.X可能每两分钟回来检查一下Y是否完成了它的工作?或者在Y打电话给他之前X不会回来?我们不知道.我们只知道在Y完成工作之前X可以做其他事情.这里X(一个模块)是非阻塞的.X和Y是两个线程还是两个进程或一个进程?我们不知道.但我们确信X和Y不能是一个线程.

3)同步:在Y回答X之前,X一直在那里等待答案.这意味着在Y完成工作之前,X无法继续.现在我们说:X和Y(两个模块)是同步的.X和Y是两个线程还是两个进程或一个线程或一个进程?我们不知道.

4)异步:在Y回答X之前,X离开那里,X可以做其他工作.在Y打电话给他之前,X不会回来.现在我们说: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);
Run Code Online (Sandbox Code Playgroud)

你可以看到这个设计是非阻塞的(你可以说这个循环的大部分时间都是无意义的,但在CPU的眼中,X正在运行,这意味着X是非阻塞的)而X和Y是同步的,因为X可以继续做任何其他事情(X不能跳出循环),直到它从Y获得书.
通常在这种情况下,使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);
Run Code Online (Sandbox Code Playgroud)

在这里的例子中,我们可以这么说

  • X1是非阻塞的
  • X1和X2是同步的
  • X和Y是异步的

如果需要,您还可以使用四个单词描述在X1中创建的线程.

更重要的是:我们何时使用同步而不是异步?我们什么时候使用阻止而不是非阻塞?

为什么Nginx不阻塞?为什么Apache阻止?

要做出一个好的选择,您必须分析您的需求并测试不同体系结构的性能.没有这样的架构适合各种需求.

  • IMO是最好的答案,因为它抓住了概念的本质:一个或两个参与者之间的关系. (5认同)
  • @VineethChitteti 你最好不要那样说。X 和 Y 可以同步,而 X 不阻塞。 (4认同)

Mac*_*Mac 15

正如您可以从众多不同(通常是相互排斥的)答案中看到的那样,这取决于您的要求.在某些领域,这些术语是同义词.或者他们可能每个都提到两个相似的概念:

  • 一种解释是,呼叫将在后台基本上无人监督地执行某些操作,以便允许程序不被其不需要控制的冗长过程所阻碍.播放音频可能就是一个例子 - 一个程序可以调用一个函数来播放(比方说)一个mp3,从那时起就可以继续使用其他东西,同时将它留给操作系统来管理在声音硬件上渲染音频的过程.
  • 另一种解释是,调用将执行程序需要监视的某些操作,但允许大多数进程在后台进行,只在程序的关键点通知程序.例如,异步文件IO可能是一个示例 - 程序向操作系统提供缓冲区以写入文件,OS仅在操作完成或发生错误时通知程序.

在任何一种情况下,目的都是允许程序不被阻止等待缓慢的过程完成 - 程序预期如何响应是唯一真正的区别.哪个术语指的是程序员,程序员,语言,平台或平台.或者术语可以指代完全不同的概念(例如,与线程编程相关的同步/异步的使用).

对不起,但我不相信有一个正确答案是全球真实的.

  • +1 好的答案。人们需要意识到“异步”可能意味着_或者_非阻塞,_或者_微软异步(基于事件/回调)方法。 (2认同)

Ant*_*ony 15

将此问题放在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
}
Run Code Online (Sandbox Code Playgroud)

使用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) {...}
    }
}
Run Code Online (Sandbox Code Playgroud)

  • `FileChannel`不可选,不能配置为非阻塞. (3认同)

Kor*_*gay 13

一个非阻塞调用立即与所有数据资料返回:字节的完整号码请求,较少或根本没有.

一个异步调用请求,将在其全部(整体)进行,但将在未来某个时间完成转移.


Bab*_*yan 12

阻塞调用:控制仅在调用完成时返回。

非阻塞调用:控制立即返回。后来操作系统以某种方式通知进程调用已完成。


同步程序:使用阻塞调用的程序。为了在调用期间不冻结,它必须有 2 个或更多线程(这就是为什么它被称为同步 - 线程同步运行)。

异步程序:使用非阻塞调用的程序。它只能有 1 个线程并且仍然保持交互。

  • 非阻塞调用:控制在做尽可能多的事情后立即返回;该方法指示完成了多少。这与异步调用不同,异步调用的行为与您描述的阻塞调用相同。 (2认同)

Fra*_*man 9

非阻塞:此功能不会在堆栈上等待.

异步:在调用离开堆栈后,工作可能会继续代表函数调用


Did*_* A. 7

同步被定义为同时发生。

异步被定义为不在同一时间发生。

这是造成第一个混乱的原因。同步实际上就是所谓的并行。当异步是顺序的时,请执行此操作,然后执行该操作。

现在,整个问题都涉及到对异步行为进行建模,因为您已经进行了一些操作,而该操作需要其他操作的响应才能开始。因此,这是一个协调问题,您如何知道现在可以开始该操作?

最简单的解决方案称为阻塞。

阻塞是指您仅选择等待另一件事完成并返回响应,然后再继续进行需要的操作。

因此,如果您需要在烤面包上涂黄油,那么您首先需要烤制烤面包。协调他们的方式是,您首先烤掉繁殖的面包,然后不断凝视烤面包机,直到烤面包机爆裂,然后再在上面放黄油。

这是最简单的解决方案,并且效果很好。没有真正的理由不使用它,除非您碰巧还需要做其他不需要与操作协调的事情。例如,洗碗。当您知道这会花费一些时间并且可以在菜盘洗完时将其洗净的时候,为什么还要不停地凝视吐司机的吐司呢?

这就是另外两个分别称为非阻塞和异步的解决方案发挥作用的地方。

非阻塞是指您在等待操作完成时选择执行其他不相关的操作。如果您认为合适,请检查响应的可用性。

因此,与其看着烤面包机使其弹出,不如将其弹出。你去洗一整盘。然后,您可以窥视烤面包机,看看是否已经吐出了烤面包。如果没有,则您去洗另一盘,检查一下各盘之间的烤面包机。当您看到烤面包突然冒出时,您就不再洗碗了,取而代之的是烤面包并继续往上面放黄油。

但是,必须不断检查烤面包会很烦人,想像一下烤面包机在另一个房间里。在两道菜之间,您浪费时间去另一个房间检查吐司。

这是异步的。

异步是指您在等待操作完成时选择执行其他不相关的事情。但是,您无需进行检查,而是将检查工作委派给其他事情,可以是操作本身或观察者,并且可以在响应可用时通知并可能干扰您,以便您可以进行其他操作:需要它。

这是一个奇怪的术语。这没有什么意义,因为所有这些解决方案都是创建相关任务异步协调的方法。这就是为什么我更喜欢将其称为事件。

因此,对于这个,您决定升级您的烤面包机,以便在烤面包完成后发出蜂鸣声。即使您正在洗碗,您也经常在听。听到哔哔声后,您便在记忆中排队,一旦您洗完当前碗碟,便会停下来将黄油涂在烤面包上。或者,您可以选择中断当前菜的清洗,并立即处理烤面包。

如果您听不到蜂鸣声,可以让您的伴侣为您看烤面包机,并在烤面包准备好时告诉您。您的合作伙伴可以自己选择以上三种策略中的任何一种,以协调其观看烤面包机并告诉您准备好的时间。

最后要说的是,很好的理解是,尽管非阻塞和异步(或我更喜欢称其为事件化)确实可以让您在等待时做其他事情,但您却没有。您可以选择不停地循环检查非阻塞呼叫的状态,而无需执行其他任何操作。但是,这通常比阻止更糟(例如,先看烤面包机,然后移开,然后再回到它,直到完成),因此许多非阻止API允许您从其过渡到阻止模式。对于事件,您可以等待空闲直到收到通知。在这种情况下,不利之处在于添加通知很复杂,并且一开始可能代价很高。您必须购买具有蜂鸣功能的新烤面包机,或者说服您的合作伙伴为您代劳。

还有一件事,您需要意识到这三个方面所提供的权衡。一个显然不比其他更好。想想我的例子。如果您的烤面包机是如此之快,您将没有时间洗碗,甚至没有开始洗碗,这就是您的烤面包机的速度。在这种情况下,开始其他事情只是浪费时间和精力。阻止就可以了。同样,如果洗碗需要的时间比烘烤的时间长10倍。您必须问自己,完成工作更重要的是什么?到那时,吐司可能会变得又冷又硬,这不值得,阻塞也可以。或者,您应该在等待时选择更快的操作。显然还有很多,但是我的回答已经很长了,我的观点是,您需要考虑所有这些,以及实现每个对象的复杂性,以确定它们是否值得,以及是否值得。

编辑:

即使已经很长,我也希望它完整,所以我还要再加两点。

1)通常还存在一种称为多路复用的第四种模型。在这种情况下,当您等待一个任务时,又启动了另一个任务,而当您同时等待两个任务时,又启动了一个任务,依此类推,直到所有任务都开始运行,然后您才等待空闲,但是在所有他们。因此,一旦完成任何操作,您就可以继续处理其响应,然后再返回等待其他响应。这被称为多路复用,因为在等待时,您需要一个接一个地检查每个任务,以查看它们是否已完成,直到完成为止。这是对普通非阻塞功能的扩展。

在我们的示例中,这就像启动烤面包机,然后是洗碗机,然后是微波炉,等等。然后等待其中的任何一个。在检查烤面包机是否完成的地方,如果没有,则需要检查洗碗机,如果没有,则要检查微波炉,然后再次检查。

2)即使我认为这是一个很大的错误,但同步经常被用来一次表示一件事。并且一次异步许多事情。因此,您将看到同步阻塞和非阻塞用于表示阻塞和非阻塞。异步阻塞和非阻塞用来表示复用和事件。

我不太了解我们如何到达那里。但是当涉及到IO和计算时,同步和异步通常指的是所谓的非重叠和重叠。也就是说,异步意味着IO和计算是重叠的,也就是同时发生。而同步意味着它们不是,因此顺序发生。对于同步非阻塞,这意味着您不启动其他IO或计算,而只是忙于等待并模拟阻塞调用。我希望人们不再像那样滥用同步和异步。因此,我不鼓励这样做。

  • 这是一个很好的类比!你刚刚敬酒了! (2认同)
  • @Helsing此外,并发与并行不同。这并不意味着两件事同时发生,而只是意味着在其中任何一件事完成之前,已经取得了不止一件事的进展。这可以通过并行化或简单地通过交错(也称为任务切换)来完成。 (2认同)