clean code-代码整洁之道 阅读笔记(第十三章)

第十三章 并发编程

"对象是过程的抽象。线程是调度的抽象。"

--James O Coplien

13.1 为什么要并发 

        并发是一种解耦策略。它帮助我们把做什么(目的)和何时(时机)做分解开。在单线
程应用中,目的与时机紧密耦合,很多时候只要查看堆栈追路即可断定应用程序的状态。

        解耦目的与时机能明显地改进应用程序的吞吐量和结构。从结构的角度来看,应用程序看起来更像是许多台协同工作的计算机,而不是一个大循环。系统因此会更易于被理解,给出了许多切分关注面的有力手段。

迷思与误解
  1. 并发总能改进性能
  2. 编写并发程序无需修改设计
  3. 在采用Web或EJB容器的时候,理解并发问题并不重要

中肯说法

  1. 并发会在性能和编写额外代码上增加一些开销
  2. 正确的并发是复杂的,即便对于简单的问题也是如此;
  3. 并发缺陷并非总能重现,所以常被看做偶发事件而忽略,未被当做真正的缺陷看待;
  4. 并发常常需要对设计策略的根本性修改
13.2 挑战
public class X{
    private int lastIdUsed;
    public int getNextId(){
        return ++lastIdUsed;
    }
}

比如,创建x的一个实体,将lastIdUsed设置为42,在两个线程中共享这个实体。假设这两个线程都调用getNextId()方法,结果可能有三种输出:

  • 线程一得到值43,线程二得到值44,lastIdUsed为44;
  • 线程一得到值44,线程二得到值43,lastIdUsed为44;
  • 线程一得到值43,线程二得到值43,lastIdUsed为43。

        就生成的字节码而言,对于在getNextId方法中执行的那两个线程,有12870种不同的可能执行路径。如果lastIdUsed的类型从int变为long,则可能路径的数量将增至2704156种。当然,多数路径都得到正确结果。问题是其中一些不能得到正确结果。

13.3 并发防御原则
13.3.1 单一权责原则

问题:

  • 并发相关代码有自己的开发、修改和调优生命周期;
  • 开发相关代码有自己要对付的挑战,和非并发相关代码不同,而且往往更为困难;
  • 即便没有周边应用程序增加的负担,写得不好的并发代码可能的出错方式数量也已经足具挑战性。

建议:分离并发相关代码与其他代码。

13.3.2 推论:限制数据作用域

        两个线程修改共享对象的同一字段时,可能互相干扰,导致未预期的行为。解决方案之一是采用synchronized关键字在代码中保护一块使用共享对象的临界区(criticalsection)。

可能出现的问题:

  1. 你会忘记保护一个或多个临界区——破坏了修改共享数据的代码码;
  2. 得多花力气保证一切都受到有效防护(破坏了DRY原则);
  3. 很难找到错误源,也很难判断错误源。

建议:谨记数据封装;严格限制对可能被共享的数据的访问。

13.3.3 推论:使用数据复本

        避免共享数据的好方法之一就是一开始就避免共享数据。在某些情形下,有可能复制对象并以只读方式对待。在另外的情况下,有可能复制对象,从多个个线程收集所有复本的结果,并在单个线程中合并这些结果。

13.3.4 推论:线程应尽可能地独立

        让每个线程在自己的世界中存在,不与其他线程共享数据。每个线程处理一个客户端请求,从不共享的源头接纳所有请求数据,存储为本地变量。这样一来,每个线程都像是世界中的唯一线程,没有同步需要。

建议:尝试将数据分解到可被独立线程(可能在不同处理器上)操作的独立子集。

13.4 了解Java库
  • 使用类库提供的线程安全群集;
  • 使用executor框架(executorframework)执行无关任务;
  • 尽可能使用非锁定解决方案;
  • 有几个类并不是线程安全的。
13.5 了解执行模型

13.5.1 生产者-消费者模型

        生产者和消费者之间的队列是一种限定资源。

13.5.2 读者-作者模型

        当存在一个主要为读者线程提供信息源,但只偶尔被作者线程更更新的共享资源,吞吐量就会是个问题。增加吞吐量,会导致线程饥饿和过时信息的累积。更新会影响吞吐量。

        挑战之处在于平衡读者线程和作者线程的需求,实现正确操作,提供合理的吞吐量,避免线程饥饿。

13.5.3 宴席哲学家

        如果没有用心设计,这种竞争式系统就会遭遇死锁、活锁、吞吐量和效率降低等问题。

可能遇到的并发问题,大多数都是这三个问题的变种。

建议:学习这些基础算法,理解其解决方案。

13.6 警惕同步方法之间的依赖

        建议:避免使用一个共享对象的多个方法。

必须使用一个共享对象的多个方法的3种手段:

  1. 基于客户端的锁定——客户端代码在调用第一个方法前锁定服务端,确保锁的范围覆盖了调用最后一个方法的代码;
  2. 基于服务端的锁定——在服务端内创建锁定服务端的方法,调用所有方法,然后解锁。让客户端代码调用新方法;
  3. 适配服务端——创建执行锁定的中间层。这是一种基于服务端的的锁定的例子,但不修改原始服务端代码。
13.7 保持同步区域微小

        关键字synchronized制造了锁。锁是昂贵的,因为它们带来了延迟和额外开销。

        另一方面,临界区应该被保护起来。所以,应该尽可能少地设计临界区。

        将同步延展到最小临界区范围之外,会增加资源争用、降低执行效率。

13.8 很难编写正确的关闭代码

        平静关闭很难做到。常见问题与死锁有关,线程一直等待永远不会到来的信号。

        建议:尽早考虑关闭问题,尽早令其工作正常。这会花费比你预期更多的时间。检视既有算法,因为这可能会比想象中难得多。

13.9 测试线程代码

        建议:编写有潜力曝露问题的测试,在不同的编程配置、系统配置和负载条件下频繁运行。如果测试失败,跟踪错误。别因为后来测试通过了后来的运行就忽略失败。

  • 将伪失败看作可能的线程问题 => 不要将系统错误归咎于偶发事件
  • 先使非线程代码可工作 => 不要同时追踪非线程缺陷和线程缺陷。
  • 编写可插拔的线程代码
  • 编写可调整的线程代码
  • 运行多于处理器数量的线程
  • 在不同平台上运行
  • 调整代码并强迫错误发生。
13.10 小结

        第一要诀是遵循单一权责原则。

        了解并发问题的可能原因。

        学习类库,了解基本算法。

        学习如何找到必须锁定的代码区域并锁定之。不要锁定不必针锁定的代码。

        要能在不同平台上、以不同配置持续重复运行线程代码。

       如果花点时间装置代码,就能极大地提升发现错误代码的机会。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32 IWDG(独立看门狗)

1 IWDG简介 STM32有两个看门狗:一个是独立看门狗(IWDG),另外一个是窗口看门狗。独立看门狗也称宠物狗,窗口看门狗也称警犬。本文主要分析独立看门狗的功能和它的应用。 独立看门狗用通俗一点的话来解释就是一个12位的…

ravynOS 0.5.0 发布 - 基于 FreeBSD 的 macOS 兼容开源操作系统

ravynOS 0.5.0 发布 - 基于 FreeBSD 的 macOS 兼容开源操作系统 ravynOS - 一个旨在提供 macOS 的精致性和 FreeBSD 的自由度的操作系统 请访问原文链接:https://sysin.org/blog/ravynos/,查看最新版。原创作品,转载请保留出处。 作者主页…

python编写的多个FastApi接口如何批量运行

fastapi编写接口并批量运行 为什么要写这fastapi接口以及拿它做什么呢? fastapi可以快速构建你自己的api,前端后端联调时,后端接口还有做好,那么这个fastapi可以快速生成mock一些数据。 结合uvicorn这个python库使用起来很方便 为…

找不到mfc140u.dll怎么修复,mfc140u.dll丢失的多种修复方法

计算机丢失mfc140u.dll文件会导致依赖该文件的软件无法正常运行。mfc140u.dll是Microsoft Visual C 2015的可再发行组件之一,它属于Microsoft Foundation Class (MFC) 库,许多使用MFC开发的程序需要这个DLL文件来正确执行。丢失了mfc140u.dll文件。会导致…

解锁最强比较工具Beyond_Compare十大功能及下载注册

Beyond Compare是一款功能强大的文件和文件夹比较工具,以下是其十大用法: 文件内容比较: Beyond Compare能够详细比较两个文件的内容,以绿色表示添加的内容,红色表示删除的内容,黄色表示修改的内容。 用户…

2732. 找到矩阵中的好子集

题目 给你一个下标从 0 开始大小为 m x n 的二进制矩阵 grid。 从原矩阵中选出若干行构成一个行的非空子集,如果子集中任何一列的和至多为子集大小的一半,那么我们称这个子集是好子集。 更正式的,如果选出来的行子集大小(即行的…

【语义分割】1-标注数据集-【单张图片】labelme标注json文件转mask

声明:我学习了b站:标注自己的语义分割数据集_哔哩哔哩_bilibili 并且复现了,记录了所思所得。 主要是说了: 做语义分割,数据集怎么用labelme标注成json文件,以及,json文件怎么转成mask 流程…

qlv格式转换成mp4格式,qlv转换成mp4格式软件工具转换器

在当今的互联网时代,视频格式转换已成为我们日常生活中的一项常见任务。其中,qlv转MP4的需求尤为突出,本文将详细介绍qlv转MP4的几种方法,帮助大家转换视频格式,我们一起来看下。 方法一: 1、使用 "小…

解决ubuntu18.04 安装vscode 报依赖库错误,以及打不开终端的问题。

其实很简单,ubuntu18.04太老了,官网最新版本的vscode对ubuntu18.04会有些依赖库的问题。 一顿查资料后发现2023.11月的1.85版本正常使用,于是完美解决。 下载链接 Visual Studio Code November 2023 点击这里下载。 下载完成,…

Verilog进行结构描述(structural modeling)(一):基本概念

目录 1.结构描述(structural modeling)的内容:2.实例 微信公众号获取更多FPGA相关源码: 1.结构描述(structural modeling)的内容: 用门来描述器件的功能基于基本元件和底层模块例化语句最接近实际的硬件结构主要使用元件的定义、使用声明以…

DCT-Net - 一键图片、视频转卡通动漫风格工具,本地一键整合包下载

只需要输入一张人物图像或者一段视频,就可以实现端到端全图卡、视频通化转换,生成二次元虚拟形象,返回卡通化后的结果图像或视频。 开发者叫menyi Fang,来自阿里巴巴通义实验室的的技术女大佬,国内大佬集成到webui&am…

基于SpringBoot的“智慧食堂”管理系统设计与实现

你好呀,我是计算机学姐码农小野!如果有相关需求,可以私信联系我。 开发语言:Java 数据库:MySQL 技术:SpringBootVue 工具:IDEA/Eclipse、Navicat、Maven 系统展示 首页 用户管理界面 菜品…

写一个坏越的个人天地(三)

昨天卡巴卡巴还是投出了学习代码以来的第一份简历,遇到好的岗位还是想争取下的吧,虽然我觉得大概率还是gg了。 昨天完成了首页的上半部分 下半部分我的构思是左右栏,左侧为菜单栏,右侧为业务栏,左侧调整右侧router进行切换内容 可以用来展示js css的小demo 稍微调整下ro…

PointCloudLib-滤波模块(Filtering)-使用体素网格过滤器对点云进行降采样

在本教程中,我们将学习如何缩减采样——即减少数量 点 – 使用体素化格网方法的点云数据集。 我们将要介绍的类在输入上创建一个 3D 体素网格(将体素网格视为空间中的一组微小 3D 框) 点云数据。然后,在每个体素(即 3D 框)中,所有点都存在 将用它们的质心近似(即下采样…

Linux驱动开发笔记(十一)tty子系统及其驱动

文章目录 前言一、串口驱动框架1.1 核心数据结构1.2 数据处理流程 二、驱动编写1. 设备树的修改2. 相关API函数3. 驱动框架4. 具体功能的实现4.1 出入口函数的编写4.2 读写函数 前言 之前已经讲过应用层的应用,接下来我们继续进行驱动的学习。其实实际上我们很少主动…

Linux显示服务器Wayland切换到X11

1、临时切换 : 注销当前用户账户,返回到登录屏幕。 在登录屏幕上,选择您要登录的用户账户。 在输入密码之前,在登录屏幕的右下角可能有一个齿轮图标,点击它以展开更多选项。 在选项中选择“Ubuntu on Xorg”或“Ubu…

从0开始学做质量工程师,只需6个月成为专业的质量管理者

欢迎来到优思学院的特别讲座——从零开始学质量工程师,只需6个月!在这篇博客中,我们将分享满满的干货,帮助你在短时间内掌握成为质量工程师所需的知识和技能。无论你是刚踏入职场的新人,还是希望提升自身竞争力的在职人…

【ACM出版】第13届亚洲膜计算会议(ACMC2024)暨 2024年机器学习、模式识别与自动化工程国际学术会议(MLPRAE 2024,8月7日-9)

第13届亚洲膜计算会议(ACMC2024)暨2024年机器学习、模式识别与自动化工程国际学术会议(MLPRAE 2024) 将于2024年8月7日-9日在新加坡举行。它致力于为机器学习、模式识别与自动化工程领域的专家和学者之间的学术交流创造一个平台。 会议的理念是让来自世…

寄存器组(堆栈指针寄存器小解)

寄存器组(堆栈指针寄存器小解) 寄存器组栈是向下伸长的出入栈操作时候的SP寄存器 例子 寄存器组 主堆栈指针(MSP):这是缺省的堆栈指针,它由 OS 内核、异常服务例程以及所有需要特权访问的 应用程序代码来使…