异步与非阻塞


373

异步调用和非阻塞调用有什么区别?还在阻塞和同步调用之间(请提供示例)?



1
我有一个很好的理解有关的差异,而读的书<Unix的网络编程>第1栏第6章

2
有趣的文章:使用异步I / O提高应用程序性能。它将I / O范例分为4类:(1)阻塞+同步,(2)非阻塞+同步,(3)阻塞+异步,以及(4)非阻塞+异步。
MS Dousti

Google专家告诉我@MSDousti,这在某些方面是错误的。
里克

@MSDousti经过研究,我认为您在评论中没有描述组合(3)和(2)。检查异步的定义,这是在谈论非阻塞。如您所见,最佳答案肯定了我的观点。轮询和回调函数仅仅是实现异步的方式/模式。是的,我是说阻塞,同步和非阻塞,异步是两对同义词。
里克(Rick)

Answers:


304

在许多情况下,它们是同一事物的不同名称,但是在某些情况下,它们是完全不同的。因此,这取决于。术语并不是在整个软件行业中以完全一致的方式应用。

例如,在经典的套接字API中,非阻塞套接字是一个简单地立即返回特殊“错误块”错误消息的套接字,而阻塞套接字会被阻塞。您必须使用单独的函数,例如select或,poll以找出何时是重试的好时机。

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

因此,它们并不总是意味着同一件事。为了说明套接字示例,我们可以说:

  • 阻塞和同步的含义相同:调用API,它将挂起线程,直到得到某种答案并将其返回给您为止。
  • 非阻塞意味着,如果无法快速返回答案,则API会立即返回错误并执行其他操作。因此,必须有一些相关的方式来查询API是否已准备好被调用(也就是说,以一种有效的方式模拟等待,以避免在紧密循环中进行手动轮询)。
  • 异步意味着API总是立即返回,并且已经开始了“后台”工作来满足您的请求,因此必须有一些相关的方法来获取结果。

就绪状态IO而非完成状态IO;在Linux上请参见libaio
将于

4
感谢您指出这些术语是上下文相关的,有时可能会不一致地使用。我特别在技术领域(但在其他领域)发现,承认这一事实通常比参加有时会发生的关于精确定义正确的辩论更为有用。
乍得NB

2
后续问题Q:答案似乎在两个术语之间有两个不同的区别。首先,通知:非阻塞意味着应用程序必须稍后再进行检查(轮询),而异步意味着我们可以忽略它,并依靠框架/ OS通过回调或发布事件来通知我们。其次,动作:非阻塞除了返回错误外什么也不做,而异步则将动作排队或在某种意义上“在后台”执行。区分术语哪个更重要?任一个区分与一个术语是否更紧密相关?还是模棱两可?
乍得NB

2
@ChadNB-术语,非阻塞与轮询紧密相关。关于API是否“记住”您的调用尝试,API记住的唯一原因是为了给您回叫。如果要调用它进行重新轮询,那么您已经必须维护必要的状态才能知道进行该后续调用,因此API也会通过维护状态而没有任何价值。
Daniel Earwicker 2012年

6
与其说非阻塞调用返回“错误”,不如说非阻塞调用实际上可以立即完成尽可能多的工作,然后指出它做了多少,将更为准确。对于某些操作,完成的工作量将是“全部”或“什么都不做”,但是某些其他操作(例如流I / O)可能会返回定量指示。如果阻塞I / O实现将允许稍后超时重试的操作(有的有的有的则不是),则非阻塞在语义上等效于以非常短的超时进行阻塞。
超级猫2014年

56

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

例如:
模块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是非阻塞的
  • X1和X2是同步的
  • X和Y是异步的

如果需要,还可以用四个词描述在X1中创建的那些线程。

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

为什么Nginx是非阻塞的?为什么要阻止Apache?

为了做出好的选择,您必须分析您的需求并测试不同体系结构的性能。没有适合各种需求的架构。


7
IMO的最佳答案,因为它抓住了这一概念的实质:一两个参与者之间的关系。
法比奥(Fábio)

在1和3中,Y都充当LIMITED资源。没有更多的Y帮助X
UVM

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

17
当涉及到I / O时,异步通常不是“并行”或“另一个线程”,主要是基于通知的。那就是:不要阻塞,不要轮询,只需要接收信号即可。当然,可以说信号来自“现实世界”,可以认为这是“另一个线索” ...
Javier 2010年

好吧,是的,我们可以整天争论确切的措辞:)
Nikolai Fetissov 2010年

但是您如何解释Linux中的AIO?同时使用了异步和非阻塞。AIO LINKS
Djvu 2014年

16
对于阅读此答案的任何人:这不是在争论确切的用词。以相同的方式,并发和并行性不是相同的概念,并且将它们区分开并不是措辞上的问题。异步性和并行性是两个不同的野兽,这个答案不准确地使它们相同。
Ptival

2
异步并不一定意味着它是并行完成的,请参见有关并行和并行编程的stackoverflow上的出色文章
BARJ

17

将这个问题放在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) {...}
    }
}

3
FileChannel是不可选择的,并且不能配置为非阻止。
michaelliu 2014年

15

正如您可能从众多不同的答案(通常是互斥的)中看到的那样,这取决于您问的是谁。在某些领域,这些术语是同义词。或者它们可能各自引用两个相似的概念:

  • 一种解释是,该调用将在后台执行一些基本上不受监督的操作,以使该程序不会因不需要控制的冗长过程而受阻。播放音频可能是一个示例-程序可以调用一个函数来播放(例如)mp3,从那时起,可以继续进行其他操作,同时将其留给OS来管理在声音硬件上渲染音频的过程。 。
  • 另一种解释是,调用将执行程序需要监视的操作,但将允许大多数过程在后台发生,而仅在过程中的关键点通知程序。例如,异步文件IO可能是一个示例-程序向操作系统提供缓冲区以写入文件,并且OS仅在操作完成或发生错误时才通知程序。

在这两种情况下,其目的都是不允许程序被阻塞,以等待缓慢的过程完成-期望程序如何响应是唯一的实际差异。哪个术语指的是哪个也从程序员到程序员,从语言到语言或从平台到平台而变化。或者这些术语可能指的是完全不同的概念(例如与线程编程有关的同步/异步使用)。

抱歉,但我不认为有一个正确的答案在全球范围内都是正确的。


1
+1好答案。人们需要知道,“异步”可能意味着无论是无阻塞,微软非同步(基于事件/回调)的方式。
工程师

14

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

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


非阻塞不返回在所有的任何结果无关
在Apartico CEO

9

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

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


1
@Marenz表示您不能直接通过posix调用进行非阻塞io。这并没有改变他在这里给出的含义。
tmc

@Marenz这仅意味着该标志对于文件将被忽略。它不会影响此答案的含义。
罗恩侯爵

8

同步被定义为同时发生。

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

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

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

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

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

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

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

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

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

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

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

这是异步的。

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

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

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

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

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

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

编辑:

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

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

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

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

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


不确定为什么您说“同步定义为同时发生”。整个想法是,它不是并发的,也不会同时发生。
赫尔辛格

这是一个很好的类比!您刚刚烤了!
D编码器,

@Helsing字面意思就是这个意思。同步意味着相同的时间,而异步意味着不同的时间:p。事物之所以异步是因为它不能同时发生,它必须在发生之前或之后发生。如果可以同时发生,则可以简单地对其进行并行化,也可以以任何顺序进行,并且不需要显式同步。这就是为什么异步编程只需要这样做,然后等待这些事情,然后等等。因为这些事情不可能同时发生。
Didier A.

@Helsing另外,并发与并行并不相同。这并不意味着同时发生两件事,而仅意味着在完成一件事情之前就已经完成了一件事情。这可以通过并行化或简单地通过交错(也称为任务切换)来完成。
Didier A.

3

阻止呼叫:仅当呼叫完成时,控件才返回。

非阻塞呼叫:控制立即返回。以后的OS以某种方式通知进程该呼叫已完成。


同步程序:使用阻塞调用的程序。为了避免在调用过程中冻结,它必须具有2个或更多线程(这就是为什么它被称为“同步-线程正在同步运行”的原因)。

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


1
非阻塞调用:控制在执行完之后立即返回,基本上可以立即执行;该方法指示完成了多少。这与异步调用不同,异步调用的行为与您描述的阻止调用相同。
supercat 2014年

0

它们仅在拼写上有所不同。他们所指的没有区别。从技术上讲,您可以说它们的重点有所不同。非阻塞是指控制流(它不会阻塞。)异步是指事件\数据在何时处理(不是同步)。


0

阻塞模型要求启动应用程序在I / O启动时阻塞。这意味着不可能同时重叠处理和I / O。同步非阻塞模型允许处理和I / O重叠,但是它要求应用程序定期检查I / O的状态。这就留下了异步非阻塞I / O,从而允许处理和I / O重叠,包括I / O完成通知。


-2

阻塞:在完成原语(同步或异步)处理后,控制权返回到调用进程

非阻塞:控件在调用后立即返回处理


10
这甚至无法回答所要求的。
Koray Tugay 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.