Scala学习笔记20: Future 和Promise

目录

    • 第二十章 Future 和Promise
      • 1- 简介
        • 1. Future: 对未来结果的承诺
        • 2. Promise: 兑现 Future 的谎言
        • 3. `Future` 和 `Promise` 的关系: 相辅相成
        • 4. 总结
      • 2- 执行上下文
        • 1. ExecutionContext 的作用:
        • 2. 常见的 ExecutionContext :
        • 3. 指定 ExecutionContext :
        • 4. 示例:
        • 5. 总结:
        • 6. 注意:
      • 3- Future
        • 1. Future: 对未来结果的承诺
        • 2. 创建 Future
        • 3. 处理 Future 结果
        • 4. 组合器详解
        • 5. 异常处理
        • 6. 示例:
        • 7. 总结
      • 4- 阻塞和异常
        • 1. 阻塞的隐患: 异步世界里的绊脚石
        • 2. 异常的捕获: 异步世界里的安全网
      • 5- Promise
        • 1. Promise 与 Future: 相辅相成的异步搭档
        • 2. 创建和完成 Promise
        • 3. 使用 Promise 进行异步编程
        • 4. Promise 的优势
        • 5. 总结
      • end
    • end

第二十章 Future 和Promise

在 Scala 中, FuturePromise 是用于处理异步操作的强大工具 ;

它们就像一对搭档, 协同工作, 优雅地管理着那些需要花费时间的任务 .

1- 简介

1. Future: 对未来结果的承诺

想象一下, 你正在请求一个需要很长时间才能返回结果的Web服务 ; 与其一直等待, 不如使用 Future ! 它就像一张期票, 承诺在未来某个时刻交付结果 .

  • 创建 Future: 你可以使用 Future { /* 耗时操作 */ } 来创建一个 Future 对象; 它会立即返回, 而不会阻塞当前线程 ;
  • 回调函数: Future 提供了 onCompleteonSuccessonFailure 等方法, 允许你在结果可用时执行相应的回调函数 ;
  • 组合 Future: 你可以使用 mapflatMaprecover 等方法来组合多个 Future , 从而构建更复杂的异步流程 ;
2. Promise: 兑现 Future 的谎言

Promise 就如同 Future 背后的担保人, 它负责最终完成 Future 并交付结果 ;

  • 创建Promise : 你可以使用 Promise[T]() 创建一个 Promise 对象, 其中 T 是结果的类型 ;
  • 完成Promise : Promise 对象有一个 success 方法, 可以用来设置 Future 的成功结果, 而 failure 方法则用于设置失败结果 ;
  • 链接 FuturePromise : Promise 对象创建后会返回一个 Future 对象; 当Promise 完成时, 与其关联的 Future 也会随之完成 ;
3. FuturePromise 的关系: 相辅相成

FuturePromise 就像一枚硬币的两面:

  • Future 代表异步操作的结果, 它是一个只读的对象 ;
  • Promise 代表对 Future 的承诺, 它是一个可写的对象 ;

通常情况下, 你会创建一个 Promise对象, 并将其 Future 对象传递给需要异步结果的代码 ; 然后, 在异步操作完成后, 使用 ``Promisesuccessfailure方法来完成 Future` .

4. 总结

FuturePromise 是 Scala 中处理异步编程的强大工具, 它们可以帮你编写更简洁、更高效的并发代码 ;

2- 执行上下文

在 Scala 中, FuturePromise 的执行上下文 (ExecutionContext) 决定了异步任务将在哪个线程池中执行 ; 正确理解和管理执行上下文对于编写高效且线程安全的异步代码至关重要 .

1. ExecutionContext 的作用:
  • 线程管理: 它维护了一个线程池, 用于执行 Future 中的异步任务 ;
  • 资源分配: 它负责为异步任务分配线程资源, 并管理线程的生命周期 ;
  • 异常处理: 它提供了默认的异常处理机制, 可以捕获 Future 中未处理的异常 ;
2. 常见的 ExecutionContext :
  • global : Scala 提供的全局默认执行上下文, 通常用于简单的异步操作 ;
  • fromExecutorService : 可以使用 Java.util.concurrent.ExecutorService 创建自定义的执行上下文, 实现更精细的线程池管理 ;
  • fork-join-pool : Scala 2.13版本引入的基于 Frok/Join 框架的执行上下文, 适合处理递归或分治任务 ;
3. 指定 ExecutionContext :
  • 隐式参数: 大多数 Future API 都接受一个隐式的 ExecutionContext 参数 ; 如果未指定, 则会使用当前作用域内的隐式值 ;
  • 显式传递: 你可以显示地将 ExcutionContext 对象传递给 Future 的方法, 例如 future.onComplete(f)(executionContext) ;
4. 示例:
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.{ExecutionContext, Future, Promise}object MyExecutionContextExample {// 自定义执行上下文val myExecutionContext: ExecutionContext = ExecutionContext.fromExecutorService(java.util.concurrent.Executors.newFixedThreadPool(10))def main(args: Array[String]): Unit = {val promise = Promise[String]()val future = promise.future// 在 myExecutionContext 中执行异步任务Future {// 执行耗时操作Thread.sleep(1000)promise.success("异步任务执行完毕")}(myExecutionContext)// 在默认的 global 上下文中处理结果future.onComplete {case scala.util.Success(value) => println(s"结果: $value") // Output: 结果: 异步任务执行完毕case scala.util.Failure(exception) => println(s"异常: ${exception.getMessage}")}Thread.sleep(2000)println("主线程继续执行") // Output: 主线程继续执行}
}
5. 总结:

ExecutionContext 是管理 FuturePromise 中异步任务执行的关键组件 ; 理解 ExecutionContext 的作用和使用方法对于编写高效、可靠的并发程序至关重要 ;

6. 注意:
  • 避免在 Future 中使用阻塞操作, 因为这会阻塞线程池中的线程, 影响程序性能 ;
  • 根据应用场景选择合适的 ExecutionContext , 例如 CPU 密集型任务可以使用固定大小的线程池, I/O 密集型任务可以使用缓存线程池 ;
  • 及时关闭自定义的 ExecutionContext , 释放线程资源, 避免资源泄露 ;

3- Future

1. Future: 对未来结果的承诺

Future 就像一张期票, 承诺在未来某个时刻交付结果 ; 它允许你发起一个耗时操作, 并在结果准备好时得到通知, 而无需阻塞当前线程 .

  • 非阻塞性: 创建 Future 对象并不会阻塞当前线程 ; 相反, 他会立即返回, 让你可以继续执行其他任务 ;
  • 结果获取: 你可以使用回调函数、Await 或组合器来获取 Future 的结果 ;
2. 创建 Future
  • Future 伴生对象: 使用 Future {/* 耗时操作 */} 语法, 可以轻松创建一个 Future ; 该操作将在隐式传入的 ExecutionContext 所管理的线程池中执行 ;
  • Promise : Promise 提供了一个更灵活的方式来创建和完成 Future ;
3. 处理 Future 结果
  • 回调函数: Future 提供了 onCompleteonSuccessonFailure 等方法, 允许你在结果可用时执行相应的回调函数 ;
  • Await : 可以使用 Await.result(future, duration) 阻塞当前线程, 直到 Future 完成并返回结果或超时 ;
  • 组合器: Future 提供了一系列组合器, 例如 mapflatMaprecover 等, 可以将多个 Future 组合在一起, 构建复杂的异步流程 ;
4. 组合器详解
  • map : 对 Future 的成功结果应用一个函数, 并返回一个新的 Future ;
  • flatMap : 对 Future 的成功结果应用一个返回 Future 的函数, 并将结果扁平化 ;
  • recover : 处理 Future 的失败情况, 并返回一个新的 Future ;
  • zip : 将两个 Future 的结果合并成一个元组 ;
  • sequence : 将一个 Future 列表转换成一个包含所有结果的 Future ;
5. 异常处理

Future 会自动捕获异步操作中抛出异常; 你可以使用 recoveronFailure 来处理这些异常 ;

6. 示例:
import java.io.File
import scala.concurrent.{Await, ExecutionContext, Future}
import scala.io.Sourceobject MyFutureExample {implicit val ec: ExecutionContext = ExecutionContext.globaldef readFileAsync(filePath: String): Future[String] = Future {val source = Source.fromFile(new File(filePath))try {source.getLines().mkString("\n")} finally {source.close()}}def main(args: Array[String]): Unit = {val futureFileContent = readFileAsync("src/main/resources/test.txt") // src/main/resources/test.txtfutureFileContent.onComplete {case scala.util.Success(fileContent) => println(s"文件内容: \n $fileContent")case scala.util.Failure(exception) => println(s"Error reading file: ${exception.getMessage}")}// 让主线程休眠 2 秒,等待异步操作完成Thread.sleep(2000)}
}

输出:

文件内容: 《题西林壁》
宋.苏轼
横看成岭侧成峰,
远近高低各不同。
不识庐山真面目,
只缘身在此山中。
7. 总结

Future 是 Scala中异步编程的基石, 它提供了一种优雅的方式来处理耗时操作, 而不会阻塞主线程 ; 掌握 Future 的创建、结果处理、组合器和异常处理机制, 可以帮助你编写出更响应更健壮的并发程序 ;

4- 阻塞和异常

1. 阻塞的隐患: 异步世界里的绊脚石

Future 的精髓在于非阻塞, 它让我们无需苦苦等待耗时操作完成, 从而提升程序的响应能力 ; 然而, 如果我们在 Future 中使用了阻塞操作, 就会破坏这种非阻塞特性, 将异步执行流拖入同步泥潭 ;

  • 线程池枯竭: 阻塞操作会长时间占用线程池中的线程, 导致其他异步任务无法及时执行, 最终可能耗尽线程资源, 使程序陷入瘫痪 ;
  • 性能瓶颈: 阻塞操作会阻塞 Future 的执行流程, 降低程序的并发处理能力, 成为性能瓶颈 ;

如何避免阻塞

  • 使用异步 API: 尽量使用异步非阻塞的API, 例如异步数据库驱动、异步 HTTP 客户端等 ;
  • 将阻塞操作移出 Future : 如果必须使用阻塞操作, 可以将其移出 Future 之外, 或使用专用的线程池来执行 ;
  • 使用 blocking 代码快: 对于无法避免的阻塞操作, 可以使用 scala.concurrent.blocking 代码块将其包裹, 告知执行上下文需要额外的线程资源 ;
2. 异常的捕获: 异步世界里的安全网

在异步编程中, 异常处理尤为重要, 因为异常可能发生在任何时间、任何线程 ; 幸运的是, Future 提供了一些机制来帮助我们捕获和处理异常, 防止程序崩溃 ;

  • recoverrecoverWith : 用于捕获 Future 内部抛出的异常, 并根据异常类型进行不同的处理 ; recover 返回一个新的 Future , 而 recoverWith 返回一个新的 Future 或抛出一个新的异常 ;
  • onFailure : 注销一个回调函数, 当 Future 执行失败时调用 ;

最佳实践

  • 始终处理异常: 不要忽略 Future 中可能抛出的异常 ;
  • 使用 try-catch 块: 在 Future 内部使用 try-catch 块来捕获异常 ;
  • 根据异常类型进行处理 : 使用 recoverrecoverWith 根据异常类型进行不同的处理 ;
  • 记录异常信息 : 记录异常信息以便于调试和排查问题 ;

5- Promise

1. Promise 与 Future: 相辅相成的异步搭档
  • Future : 代表一个异步计算的结果, 它是一个只读对象, 我们无法直接修改它的状态或结构 ;
  • promise : 代表一个对 Future 的承诺, 它是一个可以写对象, 我们可以通过它来完成 Future , 决定 Future 是成功还是失败, 以及最终的结果是什么 ;

关系: 每个 Promise 都与一个 Future 相关联, 我们可以通过 Promise 来完成 Future , 一旦 Promise 被完成, 与之关联的 Future 也会随之完成 ;

2. 创建和完成 Promise
  • 创建: 使用 Promise[T]() 创建一个新的 Promise 对象, 其中 T 代表 Future 的预期结果类型 ;
  • 完成: Promise 提供了三种完成方式 :
    • success(value: T) : 将 Future 标记为成功, 并设置结果值为 value ;
    • failure(exception: Throwable) : 将 Future 标记为失败, 并设置异常信息为 exception ;
    • complete(result: Try[T]) : 根据 result 的值 (SuccessFailure) 来完成 Future ;
3. 使用 Promise 进行异步编程

Promise 提供了一种更灵活的方式来创建和完成 Future , 特别适合于需要在多个地方协调异步操作的场景 ;

示例: 异步文件下载

import scala.concurrent.{Await, ExecutionContext, Future, Promise}
import java.io.{File, FileOutputStream}
import scala.concurrent.duration.Duration
import scala.io.Sourceobject MyPromiseExample {implicit val ec: ExecutionContext = ExecutionContext.globaldef downloadFile(url: String, filePath: String): Future[Unit] = {val promise = Promise[Unit]()Future {val file = new File(filePath)// 创建文件,如果文件已存在则不创建file.createNewFile()val outputStream = new FileOutputStream(file)try {val source = Source.fromURL(url)source.getLines().foreach(line => outputStream.write((line + "\n").getBytes()))promise.success(()) // 下载成功,完成 Promise} catch {case ex: Exception => promise.failure(ex) // 下载失败,完成 Promise} finally {outputStream.close()}}promise.future}def main(args: Array[String]): Unit = {val downloadFuture = downloadFile("https://raw.githubusercontent.com/******/files/main/poem.txt", "src/main/resources/download_poem_1.txt") // 请将路径替换为您的下载目录// 阻塞 main 函数,直到 downloadFuture 完成Await.result(downloadFuture, Duration.Inf)downloadFuture.onComplete {case scala.util.Success(_) => println("文件下载成功!")case scala.util.Failure(ex) => println(s"文件下载失败:${ex.getMessage}")}}
}
# 下载内容如下: 
《登雀鹤楼》
唐.王之涣
白日依山尽,
黄河入海流。
欲穷千里目,
更上一层楼。

解释:

  1. downloadFile 函数使用 Promise 来创建一个 Future[Unit] 对象, 表示文件下载操作 ;
  2. 在异步下载任务重, 根据下载结果调用 promise.success()promise.failure() 来完成 Promise , 从而决定 downloadFuture 的最终状态 ;
  3. main 函数中, 我们使用 onComplete 回调函数来处理下载结果 ;
4. Promise 的优势
  • 更灵活的控制: 可以自由决定 Future 的完成时间和结果 ;
  • 多点协调: 可以在多个地方完成 Promise , 实现更复杂的异步流程控制 ;
5. 总结

Promise 为我们提供了掌控 Future 命运的能力, 使得我们可以更灵活地进行异步编程 ; 理解 PromiseFuture 之间的关系, 以及如何创建、完成和使用 Promise , 可以帮助我们编写出更强大、更易于维护的异步代码 ;

end

end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/51143.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

加载chatglm3模型时出现ValueError: too many values to unpack (expected 2)的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

基于Libero的工程创建

基于Libero的工程创建 第一步:双击进入到工程界面,编写项目详细信息。 Project Name:标识您的项目名称。不要使用空格或保留的Verilog或VHDL关键字。 Project Location:在磁盘上标识您的项目位置。 Description:关于…

Linux系统下安装MySQL

前言: 本篇教程是使用Centos8来进行安装部署,如果使用的Linux系统发行版不同安装部署过程中可能会有差异,相同环境下可以跟着操作流程进行部署。本篇文章的主要目的是为了学习分享使用如有疑问欢迎提出并共同讨论。 1、安装前的准备工作 移除…

Power App学习笔记以及基础项目管理demo

Power App学习笔记以及基础项目管理demo 最近学习了一点Power App,感觉挺有意思。配置式组件开发。浅浅记录一下自己实现的项目管理系统(即Excel数据的增删改查)关于函数的一点皮毛认识。 效果图 筛选数据 编辑 详情 数据源 PowerApp 网…

SpringCloud - Nacos配置中心、命名空间

一、 Nacos配置中心 1、在gulimall-common公共模块添加依赖 <!--服务注册发现--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--…

为RTEMS Raspberrypi4 BSP添加SPI支持

为RTEMS Raspberrypi4 BSP添加SPI支持 主要参考了dev/bsps/shared/dev/spi/cadence-spi.c RTEMS 使用了基于linux的SPI框架&#xff0c;SPI总线驱动已经在内核中实现。在这个项目中我需要实习的是 RPI4的SPI主机控制器驱动 SPI在RTEMS中的实现如图&#xff1a; 首先需要将S…

day06 项目实践:router,axios

vue组件的生命周期钩子 今天几乎没有讲什么新内容&#xff0c;就是一起做项目&#xff0c;只有一个小小的知识点&#xff0c;就是关于vue组件的生命周期钩子&#xff0c;其中最重要的四个函数—— beforeCreate()&#xff1a;组件创建之间执行 created()&#xff1a;组件创建…

[Meachines] [Easy] Admirer Adminer远程Mysql反向+Python三方库函数劫持权限提升

信息收集 IP AddressOpening Ports10.10.10.187TCP:21,22,80 $ nmap -p- 10.10.10.187 --min-rate 1000 -sC -sV PORT STATE SERVICE VERSION 21/tcp open ftp vsftpd 3.0.3 22/tcp open ssh OpenSSH 7.4p1 Debian 10deb9u7 (protocol 2.0) | ssh-hostkey: | …

前端三大主流框架对比

在现代前端开发中&#xff0c;React、Vue和Angular是三大流行的框架/库。它们各自有独特的优缺点&#xff0c;适用于不同的开发需求和项目规模。下面是对这三者的详细比较&#xff1a; 一、 React 简介&#xff1a; 由Facebook开发和维护&#xff0c;是一个用于构建用户界面…

yolov8标注细胞、识别边缘、计算面积、灰度值计算

一、数据标注 1. 使用labelme软件标注每个细胞的边界信息&#xff0c;标注结果为JSON格式 2. JSON格式转yolo支持的txt格式 import json import os import glob import os.path as osp此函数用来将labelme软件标注好的数据集转换为yolov5_7.0sege中使用的数据集:param jsonfi…

【vue前端项目实战案例】之Vue仿饿了么App

本文将介绍一款仿“饿了么”商家页面的App。该案例是基于 Vue2.0 Vue Router webpack ES6 等技术栈实现的一款外卖类App&#xff0c;适合初学者进行学习。 项目源码下载链接在文章末尾 1 项目概述 该项目是一款仿“饿了么”商家页面的外卖类App&#xff0c;主要有以下功能…

【深度学习】“复杂场景下基于深度学习的卷积神经网络在鸟类多类别识别中的模型设计与性能优化研究“(上)

【深度学习】“复杂场景下基于深度学习的卷积神经网络在鸟类多类别识别中的模型设计与性能优化研究”(上) 大家好 我是寸铁&#x1f44a; 【深度学习】“复杂场景下基于深度学习的卷积神经网络在鸟类多类别识别中的模型设计与性能优化研究”(上)✨ 喜欢的小伙伴可以点点关注 &a…

LangChain-v0.2 构建 PDF 采集和问答系统

PDF 文件通常包含其他来源无法获取的重要非结构化数据。它们可能非常长&#xff0c;而且与纯文本文件不同&#xff0c;通常无法直接输入到语言模型的提示中。 在本中&#xff0c;我们将创建一个可以回答有关 PDF 文件的问题的系统。更具体地说&#xff0c;就是使用文档加载器加…

3033. 修改矩阵

3033. 修改矩阵 题目链接&#xff1a;3033. 修改矩阵 代码如下&#xff1a; class Solution { public:vector<vector<int>> modifiedMatrix(vector<vector<int>>& matrix) {vector<int> maxRows(matrix[0].size());for(int j0;j<matri…

【前端 14】Vue常见指令

Vue常见指令 Vue.js 是一个构建用户界面的渐进式框架&#xff0c;它通过一系列简洁的指令&#xff08;Directives&#xff09;来增强HTML的功能&#xff0c;使得开发者能够更加方便地构建出响应式的Web应用。本文将详细讲解Vue中的几个核心指令&#xff1a;v-bind、v-model、v…

AndroidAOSP定制之关闭某些app的通知

AndroidAOSP定制之关闭某些app的通知 前言&#xff1a; ​ 最近在做AOSP系统定制时发现gms定制好了后&#xff0c;Google应用商店用不了&#xff0c;提示此设备未获得Play保护机制认证&#xff0c;Google应用和服务无法在此设备上运行,查看官方文档和资料&#xff0c;说是由于…

【学习笔记】| 03 - 使用STM32CubeMX新建工程

使用STM32CubeMX新建工程是一个相对直观的过程&#xff0c;主要涉及到选择芯片型号、配置外设、时钟系统、GPIO引脚等&#xff0c;并最终生成工程代码。以下是一个详细的步骤指南&#xff1a; 一、打开STM32CubeMX并新建工程 启动STM32CubeMX&#xff1a; 打开STM32CubeMX软件…

FPGA实验6: 有时钟使能两位十进制计数器的设计

一、实验目的与要求 1.. 熟练掌握使用原理图设计较复杂电路&#xff1b; 2. 学习原理图设计中总线的表示以及使用方法。 二、实验原理 运用Quartus II 集成环境下的图形设计方法设计有时钟使能的两位十进制计数器。进行波形仿真和分析、引脚分配并下载到实验设备上进行功能…

[ECharts] There is a chart instance already initialized on the dom. 已存在图表,渲染重复

报错&#xff1a;已存在图表&#xff0c;渲染重复 解决: 在合适的时机执行 dispose 方法即可 // echarts 全局存入 实例 let myChart: any;// 在你的 initChart 初始化 Echarts 方法中 先执行清理方法 const initChart () > {// 执行清理方法然后初始化if(myChart){cons…

AndroidStudio 开发环境搭建

文章目录 AndroidStudio 开发环境搭建JDK 下载与安装&#xff0c;配置环境变量JDK1.8 下载安装配置环境变量新建JAVA_HOME编辑Path 下载AndroidStudio最新版本历史版本先安装JDK&#xff0c;后启动AS以管理员身份运行打开解决双击打不开的问题Error:你的主机中的软件中止了一个…