异步、非阻塞、同步、阻塞 概念

异步 / 同步


在维基百科中的释义是:在计算机编程中,异步,指的是独立于主程序流发生的事件,以及处理该事件的方式。这些可能是“外部”事件,例如信号的到达,或由程序发起的操作,该操作与程序同时/并发的执行,而程序不需要阻塞的等待结果。异步的输入(input)/输出(output)是导致异步的一个例子,让程序向存储或网络设备发出命令,而处理器继续执行程序。这样做提供了一定程度的并行性。

在一个编程接口中处理异步的一种常见方法是提供子程序(方法、函数),这些子程序(方法、函数)返回给它们的调用者一个对象,有时称为future或promise,用来表示正在进行的事件。这样的对象通常会伴随着一个同步操作,直到操作完成为止。一些编程语言,如Cilk,具有用于表示异步过程调用的特殊语法。

当您同步执行某个操作时,您需要等待它完成,然后再执行另一个任务。当您异步执行某个任务时,您可以在完成另一个任务之前继续执行

也就是说,在计算机上下文中,这意味着在另外一个“线程”上执行一个过程或任务。线程是作为工作单元存在的一系列命令(代码块)。操作系统可以管理多个线程,并在切换到另一个线程之前,为其分配一段处理器时间使它可以做一些工作。而在核心上(指的是CPU的核心),一个处理器可以简单地执行一个命令,它不需要同时做两件事。操作系统通过将时间片分配给不同的线程来模拟这个过程。
现在,如果您使用多个内核/处理器的话,那么事情实际上可能同时发生。操作系统可以将时间分配给第一个处理器上的一个线程,然后将同样的时间块分配给另一个处理器上的另一个线程。所有这些都是关于允许操作系统管理任务的完成,而您可以在代码中继续执行其他任务。

同步(单线程):

1 thread ->   |<---A---->||<----B---------->||<------C----->|

同步(多线程):

thread A -> |<---A---->|   
                        \  
thread B ------------>   ->|<----B---------->|   
                                              \   
thread C ---------------------------------->   ->|<------C----->| 

异步 (单线程):

         A-Start ------------------------------------------ A-End   
           | B-Start -----------------------------------------|--- B-End   
           |    |      C-Start ------------------- C-End      |      |   
           |    |       |                           |         |      |
           V    V       V                           V         V      V      
1 thread->|<-A-|<--B---|<-C-|-A-|-C-|--A--|-B-|--C-->|---A---->|--B-->| 

异步 (多线程):

thread A ->     |<---A---->|
 thread B ----->     |<----B---------->| 
 thread C --------->     |<------C--------->|

1.上图中的“|”标识的时间切片
2.“<” 和 “>”标识的是过程或者任务的开始于结束

异步IO

在计算机科学中,asynchronous I/O或“Non-sequential I/O”是一种input/output处理形式,它允许在传输完成之前进行其他处理。

与处理数据相比,在计算机上的input和output(I/O)操作可能非常慢。I/O设备可以合并必须进行物理移动的机械设备,例如寻找可读或写的硬盘驱动器;这通常是比电流开馆慢的数量级。例如,在一个需要执行10毫秒的磁盘操作过程中,一个时钟是一千兆赫(1Ghz)的处理器可以执行1000万个指令处理周期。
对I/O的一个简单操作是启动访问,然后等待它完成。但是当正在进行着交互时,这种方法(称为synchronous blocking I/O)会阻碍程序的进程,使系统资源闲置。当一个程序有许多I/O操作(例如程序主要或很大程度上依赖于用户输入)时,这意味着处理器需要花费几乎所有的时间来等待I/O操作完成。
作为另一种选择,您可以不需要等待IO执行完成,继续执行其他处理。这种方法称为异步输入/输出。任何取决于I / O是否完成的任务仍然需要等待(这包括使用输入值或者要求保证写操作已完成的关键操作),因此仍要阻塞,但是不依赖于I/O操作的其他处理可以继续。

异步I/O用于提高吞吐量、延迟和/或响应能力。

I/O表格:

Tables Blocking Non-Blocking
Synchronous write, read write, read + poll / select
Asynchronous - aio_write, aio_read

所有形式的异步I/O开放应用程序都可能出现资源冲突和相关故障。仔细编程(经常使用互斥、信号量等)来防止这种情况发生。
当向应用程序公开异步I/O时,有几个广泛的实现类。向应用程序提供的API的形式并不一定与操作系统实际提供的机制相一致;仿真是可能的。此外,单个应用程序可能会使用多个方法,这取决于它的需求和程序员的需求。许多操作系统提供了以上这些机制中的一个,有些可能会提供所有这些机制。

send、receive和reply操作可能是同步的或异步的。同步操作阻塞进程直到操作完成。异步操作是非阻塞的,只初始化操作。调用者可以通过稍后讨论的其他机制发现完成。
同步操作的概念需要理解操作complete(完成)意味着什么。在远程作业的情况下,当消息发送到接收方时,send和receive都会complete。在远程过程调用的情况下,假设有返回值,当结果发送回发送方时,send、receive和reply才会complete。否则,只有当程序完成执行时,send和receive才会complete。在执行过程中,发送方和接收方在约定地点,如前所述。

注意,同步/异步意味着阻塞/非阻塞,但反之亦然,也就是说,不是每个阻塞操作都是同步的,并不是每个非阻塞操作都是异步的。例如,一个send,直到接收者的机器收到消息之前都是阻塞的,但却不是同步的,因为接收进程可能没有收到它。

异步消息传递允许更多的并行性。由于进程不阻塞,它可以在消息在传输时进行一些计算。在receive的情况下,这意味着一个进程可以表示对多个端口的消息的接收感兴趣。(稍后讨论的select原语为同步接收提供了这个工具)。在同步系统中,可以通过为每个并发操作分出一个单独的进程来实现这种并行性,但是这种方法会带来额外的进程管理成本。对于线程(注意:线程也是进程,只是它成为轻量级进程)的话,这种成本通常是可以忍受的,但不是进程

异步消息传递引入了几个问题。如果消息无法传递会发生什么?发送者可能永远不会等待到消息的交付,因此永远不会得到错误。类似地,需要一种机制来通知异步接收方消息已经到达。操作调用者可以通过轮询得知completion/errors,获取软件中断,通过使用一个特殊的同步等待调用来显式地等待完成。如果应用程序需要稍后得到操作的通知,异步操作需要返回一个调用/事务的id。到通知时间时,该id将被放置在某个全局位置,或作为一个参数传递给处理器,或者等待调用。

Process:
在早期的Unix系统中。在多任务操作系统中,一个处理可能跨越多个进程,它们独立运行,有自己的内存,并处理自己的I/O流;这些流通常连接在管道中。创建和维护进程是相当昂贵的,因此,如果进程的集合小且相对稳定,那么这个解决方案很有效。它还假设单独的进程可以独立操作,除了处理相互之间的I/O操作时候;如果他们需要用其他方式交流,协调他们会变得很困难。
Polling:
轮询提供了非阻塞同步API,可用于实现一些异步API。在传统的Unix和Windows中可用。它的主要问题是,它可以在没有其他任何事情可做的情况下重复浪费CPU时间去做轮询,从而减少了其他进程可用的时间。另外,由于轮询应用程序本质上是单线程的,因此它可能无法充分利用硬件所能实现的I/O并行性。
Select(/poll) loops:
在BSD Unix中可以使用。在轮询的主题上,选择循环使用select系统调用休眠,直到出现在文件描述符(例如,当数据可用来读取)、超时发生或接收到一个信号(例如,当一个子进程死亡)时。通过检查select调用的返回参数,循环会发现哪个文件描述符已经更改并执行适当的代码。通常,为了便于使用,选择循环被实现为一个。
Signals (interrupts):
在BSD和POSIX Unix中可用。I/O是异步发出的,当它完成时,会生成一个信号(中断)。就像在底层内核编程中一样,在信号处理程序中可用的安全设施是有限的,而且进程的主流程在几乎任何时候都可能被中断,从而导致信号处理器所看到的数据结构不一致。信号处理器本身通常不能发出进一步的异步I/O。
Callback functions:
在经典的Mac OS, VMS和Windows中都可以使用。siginal方法的许多特征都是相同的,尽管很少被承认。不同的是,每个I/O请求通常可以有自己的完成回调函数,而信号系统只有一个回调。

Non-Blocking / Blocking


对于应用程序的程序猿而言,是需要一些对于等待I/O完成的控制权的。大多数I/O请求被认为是阻塞请求,这意味着在I/O完成之前,控制权不会返回到应用程序。系统调用延迟,比如read()和write()可能会很长。使用阻塞的系统调用有时称为同步编程。在大多数情况下,等待并不是真正的问题,因为程序在I/O完成之前不能做其他任何事情。但是,在一些情况下,比如使用多个客户机进行网络编程,或者使用图形用户界面编程,当它等待这用户的输入或者其他数据时候,该程序可能希望执行其他活动。

这些情况的一个解决方案是使用多个线程,这样程序的一部分就不会等待不相关的I/O完成。另一种方法是使用非阻塞系统调用的异步编程技术。异步调用立即返回,无需等待I/O完成。I/O的完成稍后将通过应用程序中的某个变量的设置或通过触发信号或者在应用程序的线性控制流之外执行的回调来进行传达。

这里写图片描述

阻塞I/O系统调用(a)直到I/O完成时才返回。非阻塞I/O系统调用立即返回。当I/O完成时,该过程将被通知。
非阻塞行为的一个很好的例子是网络套接字的select()系统调用。使用select(),应用程序可以同时监视多个资源,并且可以在不阻塞的情况下进行网络活动的轮询。select()系统调用标识数据是否挂起,然后read()或write()可能被使用,确信它们将立即完成。
PS:
正确的异步编程可能很复杂,但是由于异步程序只有一个执行线程,所以会消除死锁和数据不一致的问题。由于这个原因,有几个编程框架能够处理复杂的I/O问题,使得异步编程更加容易。
带有回调的异步编程似乎是图形化界面编程最常用的方法。它在网络编程领域也有一些支持者,但是多线程编程更常用于网络编程。

我们先来澄清一下术语。非阻塞应用程序以一种线程永远不会阻塞的方式编写——每当线程不得不阻塞I/O(例如从一个套接字读取/写入)时,它会在新数据可用时得到通知。非阻塞应用程序通常是通过消息传递(或事件)实现的。“异步”与此相关(实际上,在许多情况下,它是“非阻塞”的同义词),当您发送您的请求事件时,然后在不同的时间,在不同的线程中得到响应—异步的。然后还有一个“reactive”的时髦词——一方面是响应式函数式编程,这是相当抽象的;另一方面,有一个响应式的宣言,它定义了几乎每个应用程序的3个需求(响应性、弹性、弹性)和一个实现细节(消息驱动)。

用于实现非阻塞(web)应用程序的两个框架/工具示例是Akka(用于Scala 和 Java)和Node.js。

简单描述下工作原理。它使用了reactor模式——一个线程通过多个任务之间的多路复用来满足所有请求,而从不在任何地方阻塞。——只要有什么东西准备好了,它就会被这个线程(或者几个线程)处理。因此,如果向一个从数据库中读取数据并将数据写入响应的web应用程序发出两个请求,则该框架读取每个套接字的输入(通过获取传入数据的通知,在两个套接字之间切换),当它读取所有内容时,将“这就是请求”的消息传递给应用程序代码。然后,应用程序代码将消息发送到数据库访问层,它会将消息发送到数据库,当从数据库检索数据完成时候,它会的到一个通知。在回调中,它依次向前端/控制器发送消息,该消息反过来将数据作为响应写入,并将其作为消息发送。每件事都包含了大量的消息传递和可能的回调。

—————-未完成

猜你喜欢

转载自blog.csdn.net/qq_31179577/article/details/79099754