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


当前回答

同步被定义为同时发生(以可预测的时间或可预测的顺序)。

异步被定义为不同时发生。(不可预知的时间或顺序)。

This is what causes the first confusion, which is that asynchronous is some sort of synchronization scheme, and yes it is used to mean that, but in actuality it describes processes that are happening unpredictably with regards to when or in what order they run. And such events often need to be synchronized in order to make them behave correctly, where multiple synchronization schemes exists to do so, one of those called blocking, another called non-blocking, and yet another one confusingly called asynchronous.

因此,您可以看到,整个问题是关于找到同步异步行为的方法,因为您有一些操作需要另一个操作的响应才能开始。因此这是一个协调问题,你怎么知道你现在可以开始操作了?

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

阻塞是指您只是选择等待其他事情完成并返回响应,然后再继续执行需要它的操作。

所以如果你需要在吐司上涂黄油,那么你首先需要烤的是有教养的。你协调它们的方式是,你先烤那些有繁殖能力的面包,然后不停地盯着烤面包机,直到它把面包炸开,然后你继续往它们上面涂黄油。

这是最简单的解决方案,而且效果很好。没有真正的理由不使用它,除非你碰巧也有其他事情需要做,不需要与操作协调。例如,洗碗。为什么要一直盯着烤面包机无所事事地等着烤面包片爆开呢?你知道这需要一点时间,而且你可以在烤完的时候洗一整个盘子。

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

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

所以与其盯着烤面包机等它炸开。你去洗一整个盘子。然后你偷看烤面包机,看看烤面包有没有裂开。如果还没有,你就去洗另一个盘子,每洗一个盘子之间都要检查一下烤面包机。当你看到烤面包已经爆了,你就不再洗碗,而是拿起烤面包,继续往上面抹黄油。

不过,不断地检查烤面包片是很烦人的,想象一下烤面包机在另一个房间。在吃菜的间隙,你会浪费时间去另一个房间检查吐司。

这里出现了异步。

异步是在等待操作完成时选择执行其他不相关的事情。但是,您不是检查它,而是将检查工作委托给其他东西,可能是操作本身或监视器,并且您让那个东西通知并可能在响应可用时中断您,以便您可以继续进行需要它的其他操作。

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

所以这一次,你决定升级你的烤面包机,这样当烤面包完成时它就会发出哔哔声。你碰巧一直在听,甚至在你洗碗的时候。一听到哔哔声,你就在记忆中排队,一旦你洗完盘子,你就会停下来把黄油涂在吐司上。或者你可以选择暂停洗盘子,然后马上处理烤面包的事。

如果你听不到嘟嘟声,你可以让你的伴侣帮你看着烤面包机,并在烤好面包的时候告诉你。你的伴侣可以自己选择上述三种策略中的任何一种来协调它的任务,即看着烤面包机,并告诉你他们什么时候准备好了。

On a final note, it's good to understand that while non-blocking and async (or what I prefer to call evented) do allow you to do other things while you wait, you don't have too. You can choose to constantly loop on checking the status of a non-blocking call, doing nothing else. That's often worse than blocking though (like looking at the toaster, then away, then back at it until it's done), so a lot of non-blocking APIs allow you to transition into a blocking mode from it. For evented, you can just wait idle until you are notified. The downside in that case is that adding the notification was complex and potentially costly to begin with. You had to buy a new toaster with beep functionality, or convince your partner to watch it for you.

And one more thing, you need to realize the trade offs all three provide. One is not obviously better than the others. Think of my example. If your toaster is so fast, you won't have time to wash a dish, not even begin washing it, that's how fast your toaster is. Getting started on something else in that case is just a waste of time and effort. Blocking will do. Similarly, if washing a dish will take 10 times longer then the toasting. You have to ask yourself what's more important to get done? The toast might get cold and hard by that time, not worth it, blocking will also do. Or you should pick faster things to do while you wait. There's more obviously, but my answer is already pretty long, my point is you need to think about all that, and the complexities of implementing each to decide if its worth it, and if it'll actually improve your throughput or performance.

编辑:

虽然这已经很长了,但我也希望它是完整的,所以我再加两点。

There also commonly exists a fourth model known as multiplexed. This is when while you wait for one task, you start another, and while you wait for both, you start one more, and so on, until you've got many tasks all started and then, you wait idle, but on all of them. So as soon as any is done, you can proceed with handling its response, and then go back to waiting for the others. It's known as multiplexed, because while you wait, you need to check each task one after the other to see if they are done, ad vitam, until one is. It's a bit of an extension on top of normal non-blocking.

在我们的例子中,这就像启动烤面包机,然后是洗碗机,然后是微波炉,等等。然后伺候他们中的任何一个。你会检查烤面包机,看看它是否做好了,如果没有,你会检查洗碗机,如果没有,你会检查微波炉,然后再次检查。

尽管我认为这是一个很大的错误,但同步通常用于表示一次只做一件事。并且在同一时间异步许多事情。因此,您将看到同步阻塞和非阻塞被用来指代阻塞和非阻塞。而异步阻塞和非阻塞用来指多路复用和事件化。

I don't really understand how we got there. But when it comes to IO and Computation, synchronous and asynchronous often refer to what is better known as non-overlapped and overlapped. That is, asynchronous means that IO and Computation are overlapped, aka, happening concurrently. While synchronous means they are not, thus happening sequentially. For synchronous non-blocking, that would mean you don't start other IO or Computation, you just busy wait and simulate a blocking call. I wish people stopped misusing synchronous and asynchronous like that. So I'm not encouraging it.

Edit2:

我认为很多人都被我对同步和异步的定义搞糊涂了。让我试着说得更清楚一点。

同步被定义为以可预测的时间和/或顺序发生。意思是你知道某事什么时候开始和结束。

异步被定义为没有可预测的时间和/或顺序发生。意思是你不知道某事什么时候开始和结束。

这两种情况可以并行或并发发生,也可以依次发生。但是在同步情况下,您确切地知道事情将在什么时候发生,而在异步情况下,您不确定事情将在什么时候发生,但您仍然可以进行一些协调,至少保证一些事情只在其他事情发生之后发生(通过同步它的某些部分)。

因此,当您拥有异步流程时,异步编程允许您设置一些顺序保证,以便某些事情以正确的顺序发生,即使您不知道事情何时开始和结束。

举个例子,如果我们需要做A,那么B和C随时都可能发生。在顺序但异步的模型中,你可以有:

A -> B -> C
or
A -> C -> B
or
C -> A -> B

每次你运行这个程序,你都可以得到一个不同的,看起来是随机的。这仍然是顺序的,没有并行或并发的,但你不知道事情什么时候开始和结束,除非你让B总是发生在A之后。

如果你只添加了并发性(没有并行性),你也可以得到这样的东西:

A<start> -> C<start> -> A<end>   -> C<end>   -> B<start> -> B<end>
or
C<start> -> A<start> -> C<end>   -> A<end>   -> B<start> -> B<end>
or
A<start> -> A<end>   -> B<start> -> C<start> -> B<end>   -> C<end>
etc...

同样,你不知道事情什么时候开始和结束,但你已经让B总是在A结束后开始,但那不一定是在A结束后立即开始,它是在A结束后的某个未知时间,B可以完全或部分地发生在两者之间。

如果你添加了并行,现在你就有了这样的东西:

A<start> -> A<end>   -> B<start>       -> B<end>         ->
            C<start> -> C<keeps going> -> C<keeps going> -> C<end>
or
A<start> -> A<end>         -> B<start> -> B<end>
C<start> -> C<keeps going> -> C<end>
etc...

现在,如果我们看看同步的情况,在顺序设置中,你会有:

A -> B -> C

这个顺序总是这样的,每次你运行程序,你会得到A, B, C,尽管从需求的概念上讲C可以在任何时候发生,在同步模型中你仍然可以准确地定义它开始和结束的时间。当然,你可以这样指定它:

C -> A -> B

相反,但由于它是同步的,那么这个顺序将是每次运行程序时的顺序,除非您再次更改代码以显式更改顺序。

现在如果你把并发添加到同步模型中,你可以得到:

C<start> -> A<start> -> C<end> -> A<end> -> B<start> -> B<end>

再说一次,不管你运行了多少次这个顺序都是这样的。类似地,你可以在代码中显式地更改它,但它在整个程序执行中是一致的。

最后,如果你将并行性也添加到同步模型中,你会得到:

A<start> -> A<end> -> B<start> -> B<end>
C<start> -> C<end>

同样,在运行的每个程序上都是如此。这里的一个重要方面是,要使其完全同步,这意味着B必须在A和C结束后开始。如果C是一个可以更快或更慢地完成的操作,这取决于机器的CPU功率,或其他性能考虑因素,要使它同步,你仍然需要让B等待它结束,否则你又会得到一个异步行为,其中不是所有的时间都是确定的。

在协调CPU操作和CPU时钟时,你会得到这种同步的东西,你必须确保你能在下一个时钟周期内及时完成每个操作,否则你需要再延迟一个时钟来为这个操作提供空间,如果你不这样做,你就会搞砸你的同步行为,如果事情依赖于这个顺序,它们就会中断。

Finally, lots of systems have synchronous and asynchronous behavior mixed in, so if you have any kind of inherently unpredictable events, like when a user will click a button, or when a remote API will return a response, but you need things to have guaranteed ordering, you will basically need a way to synchronize the asynchronous behavior so it guarantees order and timing as needed. Some strategies to synchronize those are what I talk about previously, you have blocking, non-blocking, async, multiplexed, etc. See the emphasis on "async", this is what I mean by the word being confusing. Somebody decided to call a strategy to synchronize asynchronous processes "async". This then wrongly made people think that asynchronous meant concurrent and synchronous meant sequential, or that somehow blocking was the opposite of asynchronous, where as I just explained, synchronous and asynchronous in reality is a different concept that relates to the timing of things as being in sync (in time with each other, either on some shared clock or in a predictable order) or out of sync (not on some shared clock or in an unpredictable order). Where as asynchronous programming is a strategy to synchronize two events that are themselves asynchronous (happening at an unpredictable time and/or order), and for which we need to add some guarantees of when they might happen or at least in what order.

所以我们只剩下两件使用“异步”一词的事情:

异步进程:我们不知道它们将在什么时间开始和结束的进程,因此也不知道它们将以什么顺序结束运行。 异步编程:一种编程风格,它允许您使用回调或观察者来同步两个异步进程,这些回调或观察者会中断执行程序,以便让执行程序知道已经完成了一些事情,这样您就可以在进程之间添加可预测的顺序。

其他回答

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

非阻塞:调用后控制权立即返回进程

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

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


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

异步程序:使用非阻塞调用的程序。它可以只有一个线程,但仍然保持交互。

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

非阻塞:该函数在栈上时不会等待。

异步:在函数调用离开堆栈后,该函数调用的工作可以继续进行

同步被定义为同时发生(以可预测的时间或可预测的顺序)。

异步被定义为不同时发生。(不可预知的时间或顺序)。

This is what causes the first confusion, which is that asynchronous is some sort of synchronization scheme, and yes it is used to mean that, but in actuality it describes processes that are happening unpredictably with regards to when or in what order they run. And such events often need to be synchronized in order to make them behave correctly, where multiple synchronization schemes exists to do so, one of those called blocking, another called non-blocking, and yet another one confusingly called asynchronous.

因此,您可以看到,整个问题是关于找到同步异步行为的方法,因为您有一些操作需要另一个操作的响应才能开始。因此这是一个协调问题,你怎么知道你现在可以开始操作了?

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

阻塞是指您只是选择等待其他事情完成并返回响应,然后再继续执行需要它的操作。

所以如果你需要在吐司上涂黄油,那么你首先需要烤的是有教养的。你协调它们的方式是,你先烤那些有繁殖能力的面包,然后不停地盯着烤面包机,直到它把面包炸开,然后你继续往它们上面涂黄油。

这是最简单的解决方案,而且效果很好。没有真正的理由不使用它,除非你碰巧也有其他事情需要做,不需要与操作协调。例如,洗碗。为什么要一直盯着烤面包机无所事事地等着烤面包片爆开呢?你知道这需要一点时间,而且你可以在烤完的时候洗一整个盘子。

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

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

所以与其盯着烤面包机等它炸开。你去洗一整个盘子。然后你偷看烤面包机,看看烤面包有没有裂开。如果还没有,你就去洗另一个盘子,每洗一个盘子之间都要检查一下烤面包机。当你看到烤面包已经爆了,你就不再洗碗,而是拿起烤面包,继续往上面抹黄油。

不过,不断地检查烤面包片是很烦人的,想象一下烤面包机在另一个房间。在吃菜的间隙,你会浪费时间去另一个房间检查吐司。

这里出现了异步。

异步是在等待操作完成时选择执行其他不相关的事情。但是,您不是检查它,而是将检查工作委托给其他东西,可能是操作本身或监视器,并且您让那个东西通知并可能在响应可用时中断您,以便您可以继续进行需要它的其他操作。

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

所以这一次,你决定升级你的烤面包机,这样当烤面包完成时它就会发出哔哔声。你碰巧一直在听,甚至在你洗碗的时候。一听到哔哔声,你就在记忆中排队,一旦你洗完盘子,你就会停下来把黄油涂在吐司上。或者你可以选择暂停洗盘子,然后马上处理烤面包的事。

如果你听不到嘟嘟声,你可以让你的伴侣帮你看着烤面包机,并在烤好面包的时候告诉你。你的伴侣可以自己选择上述三种策略中的任何一种来协调它的任务,即看着烤面包机,并告诉你他们什么时候准备好了。

On a final note, it's good to understand that while non-blocking and async (or what I prefer to call evented) do allow you to do other things while you wait, you don't have too. You can choose to constantly loop on checking the status of a non-blocking call, doing nothing else. That's often worse than blocking though (like looking at the toaster, then away, then back at it until it's done), so a lot of non-blocking APIs allow you to transition into a blocking mode from it. For evented, you can just wait idle until you are notified. The downside in that case is that adding the notification was complex and potentially costly to begin with. You had to buy a new toaster with beep functionality, or convince your partner to watch it for you.

And one more thing, you need to realize the trade offs all three provide. One is not obviously better than the others. Think of my example. If your toaster is so fast, you won't have time to wash a dish, not even begin washing it, that's how fast your toaster is. Getting started on something else in that case is just a waste of time and effort. Blocking will do. Similarly, if washing a dish will take 10 times longer then the toasting. You have to ask yourself what's more important to get done? The toast might get cold and hard by that time, not worth it, blocking will also do. Or you should pick faster things to do while you wait. There's more obviously, but my answer is already pretty long, my point is you need to think about all that, and the complexities of implementing each to decide if its worth it, and if it'll actually improve your throughput or performance.

编辑:

虽然这已经很长了,但我也希望它是完整的,所以我再加两点。

There also commonly exists a fourth model known as multiplexed. This is when while you wait for one task, you start another, and while you wait for both, you start one more, and so on, until you've got many tasks all started and then, you wait idle, but on all of them. So as soon as any is done, you can proceed with handling its response, and then go back to waiting for the others. It's known as multiplexed, because while you wait, you need to check each task one after the other to see if they are done, ad vitam, until one is. It's a bit of an extension on top of normal non-blocking.

在我们的例子中,这就像启动烤面包机,然后是洗碗机,然后是微波炉,等等。然后伺候他们中的任何一个。你会检查烤面包机,看看它是否做好了,如果没有,你会检查洗碗机,如果没有,你会检查微波炉,然后再次检查。

尽管我认为这是一个很大的错误,但同步通常用于表示一次只做一件事。并且在同一时间异步许多事情。因此,您将看到同步阻塞和非阻塞被用来指代阻塞和非阻塞。而异步阻塞和非阻塞用来指多路复用和事件化。

I don't really understand how we got there. But when it comes to IO and Computation, synchronous and asynchronous often refer to what is better known as non-overlapped and overlapped. That is, asynchronous means that IO and Computation are overlapped, aka, happening concurrently. While synchronous means they are not, thus happening sequentially. For synchronous non-blocking, that would mean you don't start other IO or Computation, you just busy wait and simulate a blocking call. I wish people stopped misusing synchronous and asynchronous like that. So I'm not encouraging it.

Edit2:

我认为很多人都被我对同步和异步的定义搞糊涂了。让我试着说得更清楚一点。

同步被定义为以可预测的时间和/或顺序发生。意思是你知道某事什么时候开始和结束。

异步被定义为没有可预测的时间和/或顺序发生。意思是你不知道某事什么时候开始和结束。

这两种情况可以并行或并发发生,也可以依次发生。但是在同步情况下,您确切地知道事情将在什么时候发生,而在异步情况下,您不确定事情将在什么时候发生,但您仍然可以进行一些协调,至少保证一些事情只在其他事情发生之后发生(通过同步它的某些部分)。

因此,当您拥有异步流程时,异步编程允许您设置一些顺序保证,以便某些事情以正确的顺序发生,即使您不知道事情何时开始和结束。

举个例子,如果我们需要做A,那么B和C随时都可能发生。在顺序但异步的模型中,你可以有:

A -> B -> C
or
A -> C -> B
or
C -> A -> B

每次你运行这个程序,你都可以得到一个不同的,看起来是随机的。这仍然是顺序的,没有并行或并发的,但你不知道事情什么时候开始和结束,除非你让B总是发生在A之后。

如果你只添加了并发性(没有并行性),你也可以得到这样的东西:

A<start> -> C<start> -> A<end>   -> C<end>   -> B<start> -> B<end>
or
C<start> -> A<start> -> C<end>   -> A<end>   -> B<start> -> B<end>
or
A<start> -> A<end>   -> B<start> -> C<start> -> B<end>   -> C<end>
etc...

同样,你不知道事情什么时候开始和结束,但你已经让B总是在A结束后开始,但那不一定是在A结束后立即开始,它是在A结束后的某个未知时间,B可以完全或部分地发生在两者之间。

如果你添加了并行,现在你就有了这样的东西:

A<start> -> A<end>   -> B<start>       -> B<end>         ->
            C<start> -> C<keeps going> -> C<keeps going> -> C<end>
or
A<start> -> A<end>         -> B<start> -> B<end>
C<start> -> C<keeps going> -> C<end>
etc...

现在,如果我们看看同步的情况,在顺序设置中,你会有:

A -> B -> C

这个顺序总是这样的,每次你运行程序,你会得到A, B, C,尽管从需求的概念上讲C可以在任何时候发生,在同步模型中你仍然可以准确地定义它开始和结束的时间。当然,你可以这样指定它:

C -> A -> B

相反,但由于它是同步的,那么这个顺序将是每次运行程序时的顺序,除非您再次更改代码以显式更改顺序。

现在如果你把并发添加到同步模型中,你可以得到:

C<start> -> A<start> -> C<end> -> A<end> -> B<start> -> B<end>

再说一次,不管你运行了多少次这个顺序都是这样的。类似地,你可以在代码中显式地更改它,但它在整个程序执行中是一致的。

最后,如果你将并行性也添加到同步模型中,你会得到:

A<start> -> A<end> -> B<start> -> B<end>
C<start> -> C<end>

同样,在运行的每个程序上都是如此。这里的一个重要方面是,要使其完全同步,这意味着B必须在A和C结束后开始。如果C是一个可以更快或更慢地完成的操作,这取决于机器的CPU功率,或其他性能考虑因素,要使它同步,你仍然需要让B等待它结束,否则你又会得到一个异步行为,其中不是所有的时间都是确定的。

在协调CPU操作和CPU时钟时,你会得到这种同步的东西,你必须确保你能在下一个时钟周期内及时完成每个操作,否则你需要再延迟一个时钟来为这个操作提供空间,如果你不这样做,你就会搞砸你的同步行为,如果事情依赖于这个顺序,它们就会中断。

Finally, lots of systems have synchronous and asynchronous behavior mixed in, so if you have any kind of inherently unpredictable events, like when a user will click a button, or when a remote API will return a response, but you need things to have guaranteed ordering, you will basically need a way to synchronize the asynchronous behavior so it guarantees order and timing as needed. Some strategies to synchronize those are what I talk about previously, you have blocking, non-blocking, async, multiplexed, etc. See the emphasis on "async", this is what I mean by the word being confusing. Somebody decided to call a strategy to synchronize asynchronous processes "async". This then wrongly made people think that asynchronous meant concurrent and synchronous meant sequential, or that somehow blocking was the opposite of asynchronous, where as I just explained, synchronous and asynchronous in reality is a different concept that relates to the timing of things as being in sync (in time with each other, either on some shared clock or in a predictable order) or out of sync (not on some shared clock or in an unpredictable order). Where as asynchronous programming is a strategy to synchronize two events that are themselves asynchronous (happening at an unpredictable time and/or order), and for which we need to add some guarantees of when they might happen or at least in what order.

所以我们只剩下两件使用“异步”一词的事情:

异步进程:我们不知道它们将在什么时间开始和结束的进程,因此也不知道它们将以什么顺序结束运行。 异步编程:一种编程风格,它允许您使用回调或观察者来同步两个异步进程,这些回调或观察者会中断执行程序,以便让执行程序知道已经完成了一些事情,这样您就可以在进程之间添加可预测的顺序。