当您说某个特定的框架是"异步","非阻塞"和"事件驱动"时,请确保使用正确的词。
当您的同事将某种东西描述为非阻塞的异步I / O时,这意味着什么。 是否可以使用阻塞的异步I / O? 如果不是,非阻塞和异步意味着同一件事吗? 在本文中,我将解释这些(以及许多其他)常用(经常被滥用)的软件工程术语的正确含义和用法。
并发与并行执行并发-重叠执行或时间分片。 并发是一种编程属性,即使对于单核计算机也可能发生。
并行性-同时执行。 这是执行硬件的属性,其中任务实际上同时进行。
同步与AsyncSynchronous-在同步操作的情况下,发起操作的一方必须等待操作完成。
异步-在异步操作中,启动器不需要等待操作完成。 它可以继续进行下一步,而无需等待对方的结果。 稍后可以通过其他某种机制来检测操作的完成。 除了编程,该概念还可以应用于其他领域。 电子邮件是异步通信的一个示例,而电话对话是同步的。
阻塞与非阻塞这是一个编程概念,与编写代码的方式有关(与同步与异步不同)。 如果函数调用立即返回一个值,则称为非阻塞。 在某些情况下,例如I / O,功能的逻辑完成结果无法立即获得。 但是,它将引用返回到占位符,该值稍后将可用。 例如,Java Future。 即使该值仅在10秒钟后可用,以下函数也会立即返回。
- public Future<Integer> calculate(Integer input) {
- return executor.submit(() -> {
- Thread.sleep(10000); return input * input;
- });
- }
异步函数调用不能阻塞。
取决于上下文,非阻塞和异步可能意味着相同,也可能不同。 虽然电子邮件通信是异步的,但将电子邮件称为非阻塞性呼叫并没有任何意义。
命令式与声明式编程风格
声明式编程是一种编程范例,用于表达计算的逻辑(要做什么)而不描述其控制流程(如何做)。 例如SQL命令。
命令式编程是一种编程范式,它根据更改程序状态的语句来描述计算。
下面的示例循环遍历数字1到10,并找到偶数。
- List<Integer> numbersOneThroughTen = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
- //With imperative programming, we'd step through this, and decide //what we want:
- List<Integer> evenNumbers = new ArrayList<>();
- for (Integer eachValue : numbersOneThroughTen) {
- if (eachValue % 2 == 0) {
- evenNumbers.add(eachValue);
- }
- }
- //The following code uses declarative programming to accomplish the //same thing.
- // Here, we're saying "Give us everything where it's odd"
- evenNumbers = numbersOneThroughTen.stream().filter(num -> num % 2 == 0)
- .collect(Collectors.toList());
注意:这是一个抽象概念。 在上面的示例中,您可以进一步说我们没有得到" foreach"的实际实现,因此从某种意义上说,它是在描述"做什么"而不是"如何做"。 从纯粹的命令式到纯粹的声明式,每种编程风格都处在某种范围内。 函数式编程比过程式编程更具声明性。
功能编程一种程序设计范例,其中通过应用和组合功能来构造程序。 在函数式编程中,将函数视为一等公民,这意味着它们可以绑定到名称(包括本地标识符),作为参数传递并从其他函数返回,就像任何其他数据类型一样。 这允许程序以声明性和可组合的方式编写,其中将小的功能组合在一起以创建更大的功能和程序。 纯函数式编程是所有函数均为纯函数的函数式编程的子集。 我将写另一篇有关函数式编程的详细博客文章。
事件驱动或基于消息的体系结构事件是系统状态的重大变化。 例如,当消费者购买汽车时,汽车的状态将从"待售"变为"已售出"。 汽车经销商的系统体系结构可以将此状态更改视为一个事件,该事件的发生可以被体系结构内的其他应用程序知道。 严格来说,事件无法传播,只会发生。 发出,处理和传播的实际上是事件的通知-以纯文本消息的形式。 在事件驱动的系统中,不同的参与组件通过异步消息进行通信。
响应式系统与响应式编程响应式系统是一种程序,其体系结构允许它对运行时环境中的更改做出反应。 反应性系统(http://www.reactivemanifesto.org)中正式规定了反应性系统应具有的属性。 这些属性中的三个可以概括为响应,弹性和弹性。
响应式意味着响应式系统可以实时响应输入,而不是延迟一个简单的查询,因为该系统正在为其他人处理大量工作。
弹性意味着系统通常不会因为一个组件发生故障而失败; 断开的网络链接不会影响不涉及该链接的查询,对无响应组件的查询可以重新路由到备用组件。
弹性意味着系统可以适应其工作负载的变化并继续有效执行。 由于您可能会在提供食物和提供饮料之间的栏中动态地重新分配人员,以使两行的等待时间都相似,因此您可以调整与各种软件服务相关的工作线程数,以确保没有工人闲置,同时确保每个队列继续 待处理。
显然,这些属性可以通过多种方式实现,但是一种主要方法是使用反应式编程风格。
响应式编程是使用表示为异步消息的事件进行编程(如在事件驱动的体系结构中)。 这些消息通常被建模为数据流。 例如,在Web应用程序中,典型的单击事件可以建模为数据流,您可以在该数据流上观察并产生一些副作用。 您可以创建任何数据流,而不仅仅是单击和悬停事件。 流既便宜又无处不在,任何事物都可以是流:变量,用户输入,属性,缓存,数据结构等。例如,您的Twitter feed是一种与单击事件相同的数据流。 您可以收听该流并做出相应的反应。
流可以用作另一流的输入。 甚至多个流也可以用作另一个流的输入。 您可以合并两个流。 您可以过滤流以获得另一个只包含您感兴趣的事件的流。您可以将数据值从一个流映射到另一个新流。