异步调用和非阻塞调用之间的区别是什么?在阻塞和同步调用之间(请提供示例)?
当前回答
阻塞模型要求初始应用程序在I/O开始时阻塞。这意味着不可能同时重叠处理和I/O。同步非阻塞模型允许处理和I/O重叠,但它要求应用程序反复检查I/O的状态。这就留下了异步非阻塞I/O,允许处理和I/O重叠,包括I/O完成的通知。
其他回答
同步被定义为同时发生(以可预测的时间或可预测的顺序)。
异步被定义为不同时发生。(不可预知的时间或顺序)。
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.
所以我们只剩下两件使用“异步”一词的事情:
异步进程:我们不知道它们将在什么时间开始和结束的进程,因此也不知道它们将以什么顺序结束运行。 异步编程:一种编程风格,它允许您使用回调或观察者来同步两个异步进程,这些回调或观察者会中断执行程序,以便让执行程序知道已经完成了一些事情,这样您就可以在进程之间添加可预测的顺序。
同步/异步是用来描述两个模块之间的关系。 阻塞/非阻塞是描述一个模块的情况。
一个例子: 模块X: I。 模块Y:“书店”。 X问Y:你有《c++入门》这本书吗?
blocking: before Y answers X, X keeps waiting there for the answer. Now X (one module) is blocking. X and Y are two threads or two processes or one thread or one process? we DON'T know. non-blocking: before Y answers X, X just leaves there and do other things. X may come back every two minutes to check if Y has finished its job? Or X won't come back until Y calls him? We don't know. We only know that X can do other things before Y finishes its job. Here X (one module) is non-blocking. X and Y are two threads or two processes or one process? we DON'T know. BUT we are sure that X and Y couldn't be one thread. synchronous: before Y answers X, X keeps waiting there for the answer. It means that X can't continue until Y finishes its job. Now we say: X and Y (two modules) are synchronous. X and Y are two threads or two processes or one thread or one process? we DON'T know. asynchronous: before Y answers X, X leaves there and X can do other jobs. X won't come back until Y calls him. Now we say: X and Y (two modules) are asynchronous. X and Y are two threads or two processes or one process? we DON'T know. BUT we are sure that X and Y couldn't be one thread.
请注意上面两个加粗的句子。为什么题目中的粗体字包含两种情况,而题目中的粗体字只包含一种情况?这是区分非阻塞和异步的关键。
让我试着用另一种方式来解释这四个词:
blocking: OMG, I'm frozen! I can't move! I have to wait for that specific event to happen. If that happens, I would be saved! non-blocking: I was told that I had to wait for that specific event to happen. OK, I understand and I promise that I would wait for that. But while waiting, I can still do some other things, I'm not frozen, I'm still alive, I can jump, I can walk, I can sing a song etc. synchronous: My mom is gonna cook, she sends me to buy some meat. I just said to my mom: We are synchronous! I'm so sorry but you have to wait even if I might need 100 years to get some meat back... asynchronous: We will make a pizza, we need tomato and cheeze. Now I say: Let's go shopping. I'll buy some tomatoes and you will buy some cheeze. We needn't wait for each other because we are asynchronous.
下面是一个关于非阻塞和同步的典型例子:
// thread X
while (true)
{
msg = recv(Y, NON_BLOCKING_FLAG);
if (msg is not empty)
{
break;
}
else
{
sleep(2000); // 2 sec
}
}
// thread Y
// prepare the book for X
send(X, book);
You can see that this design is non-blocking (you can say that most of time this loop does something nonsense but in CPU's eyes, X is running, which means that X is non-blocking. If you want you can replace sleep(2000) with any other code) whereas X and Y (two modules) are synchronous because X can't continue to do any other things (X can't jump out of the loop) until it gets the book from Y. Normally in this case, making X blocking is much better because non-blocking spends much resource for a stupid loop. But this example is good to help you understand the fact: non-blocking doesn't mean asynchronous.
这四个字确实很容易让我们困惑,我们应该记住的是,这四个字是为建筑设计服务的。学习如何设计一个好的架构是区分它们的唯一方法。
例如,我们可以设计这样一种架构:
// 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
}
else
{
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中创建的线程。
再说一次:这四个字是为建筑设计服务的。所以我们需要的是一个合适的架构,而不是像语言律师一样区分这四个词。如果你遇到一些情况,你不能很清楚地区分这四个词,你应该忘记这四个词,用你自己的话来描述你的架构。
所以更重要的事情是:我们什么时候使用同步而不是异步?什么时候用阻塞代替非阻塞?X1的阻塞性比非阻塞性更好吗?X和Y是同步的还是异步的?为什么Nginx是非阻塞的?Apache为什么阻塞?这些问题是你必须弄清楚的。
为了做出正确的选择,您必须分析您的需求并测试不同体系结构的性能。没有这样一种体系结构可以满足各种需求。
阻塞模型要求初始应用程序在I/O开始时阻塞。这意味着不可能同时重叠处理和I/O。同步非阻塞模型允许处理和I/O重叠,但它要求应用程序反复检查I/O的状态。这就留下了异步非阻塞I/O,允许处理和I/O重叠,包括I/O完成的通知。
阻塞:在原语(同步或异步)处理完成后,控制返回到调用进程
非阻塞:调用后控制权立即返回进程
非阻塞:该函数在栈上时不会等待。
异步:在函数调用离开堆栈后,该函数调用的工作可以继续进行
推荐文章
- 如何使HTTP请求在PHP和不等待响应
- 反应-显示加载屏幕,而DOM是渲染?
- 如何正确地读取异步/等待文件?
- 使用Moq模拟单元测试的异步方法
- 如何使用JUnit来测试异步进程
- Kotlin协程中的启动/连接和异步/等待之间有什么区别
- CompletableFuture, Future和RxJava的Observable之间的区别
- 为什么要在c#中使用Task<T>而不是ValueTask<T> ?
- Asyncio。Gather vs asyncio.wait
- 同步调用异步方法
- 所有异步forEach回调完成后的回调
- 并发、并行和异步方法之间的区别是什么?
- 同步调用一个异步Javascript函数
- 异步等待在linq选择
- 为什么使用异步和返回等待,当你可以直接返回任务<T> ?