《计算机构造与解释》读书笔记(4)

news2024/9/26 5:22:18

1. 写在最前面

真正要做的事情,对神明都不要讲。一定如此,万般如此。

2. 并发:时间是一个本质问题

引入赋值之后,必须承认时间在所用的计算模型中的位置。在引入赋值之前,所有程序都没有时间问题,任何具有某个值的表达式,将总是具有这个值。

引入赋值之后带来的问题,以 3.1.1 节中模拟从银行账户提款并返回最后余额为例:

(withdraw 25)
75
(withdraw 25)
50

连续对同一个表达式求值,却产生了不同的值。这种行为的出现就是因为:赋值语句的执行描绘出有关值变化的时刻,对一个表达式的求值结果不但依赖于该表达式本身,还依赖于求值发生咋这些时刻之前还是之后。

现实世界里的对象并不是一次一个地顺序变化,与此相反,它们总是发并地活动,所有东西一起活动。

注:可以通过将模型组织为一些具有相互分离的局部状态对象,使做出的程序更加模块化。

​ 将计算模型划分为一些能各自独立地并发演化的部分

2.1 并发系统中时间的性质

事件顺序的非确定性,可能对并发系统的设计提出了严重的问题。举例证明,假定由 Peter 和 Paul 进行取款被实现为两个独立的进程,它们共享同一个变量 balance,这两个计算进程都由如下过程描述:

(define (withdraw amount)
  (if (>= balance amount)
      (begin (set! balance (- balance amount))
             balance)
       "Insufficent funds"))

上述表达式包含三个步骤:

  • 取得变量 balance 的值
  • 计算出新的余额
  • 将变量 balance 设置为新值

如果 Peter 和 Paul 在提款过程中并发执行这一语句,那么这两次提款在访问 balance 和将它设置为新值的动作就可能交错。

下图的时序图勾画了一个事件顺序,其中的 balance 在开始时是 100,Peter 取走了 10,Paul 取走了 25,然后 balance 最后的值却是 75。

在这里插入图片描述

上述实例表现出的一般性现象是:几个进程有可能共享同一个状态变量。使事情变得更加复杂的原因,就是多个进程有可能同时试图去操作这种共享状态。

2.1.1 并发程序的正确行为

在写那些使用 set! 的程序时必须小心,因为一个计算的结果依赖于其中的各个赋值发生的顺序。**对于并发进程,对于赋值就需要特别小心,因为无法控制其他进程所做赋值的出现顺序。**如果几个修改可能并发出现,必须采用某些方式,以设法保证系统的行为是正确的。

对于并发的一种可能限制方式是规定,修改任意共享状态变量的两个操作都不允许同时发生。

注:这是一个非常严厉的要求,以分布式银行系统举例,这就要求系统设计者保证同时出现的只能有一个交易,这样做可能过于低效,也太保守了。

对于并发的另一种不那么严厉的限制方式是,保证并发系统产生的结果与各个进程按照某种方式顺序运行产生出的结果完全一样。这一要求中包含两个重要方面:

  • 它并没有要求各个进程实际上顺序地运行,而只是要求它们产生的结果与假设它们顺序运行所产生的结果相同。
  • 一个并发程序完全可能产生多于一个「正确的」结果,只要求其结果与按照某种方式顺序化的结果相同。

注:参考 raft 算法,节点间数据的同步

对于并发程序的正确执行,还可以提出一些更弱的要求。一个模拟扩缩过程的程序可以由一大批进程组成,每个进程代表空间中很小的一点体积,它们并发地更新自己的值。这里的每个进程都反复将自己的值更新为自己的原值和相邻进程的值的平均值。无论有关的操作按什么顺序执行,这种算法都能收敛到正确的解,因此也就不需要对共享变量的并发使用提出任何限制了

2.2 控制并发的机制

2.2.1 对共享变量的串行访问

串行化:使进程可以并发地执行,但是其中也有一些过程不能并发地执行。

串行化的例子:创建一些不同的过程集合,并且保证在每个时刻,在任何一个串行化集合里至多只有一个过程的执行。如果某个集合里有过程正在执行,而另一进程企图执行这个集合里的任何过程时,它就必须等待到前一过程的执行结束。

2.2.2 Schema 里的串行化

为了使上述「对共享变量的串行访问」机制更加具象化,假定扩充本书示例中所用的 Scheme 语言,加入一个称为 parallel-execute 的过程:

(parallel-execute <p1> <p2> ... <pk>)

这里的的每个 <p> 必须是一个无参过程,parallel-execute 为每个 <p> 创建一个独立的过程,该进程将应用 <p>

以下面的例子为例:

(define x 10)
(parallel-execute (lambda () (set! x (* x x)))
                  (lambda () (set! x (+ x 1))))

假设 P1 要把 x 设置为 x * x,P2 要把 x 设置为 + x 1,在上述的例子执行过之后,x 将具有以下 5 种值之一:

101:P1 将 x 设置为 100,而后 P2 将 x 的值增加到 101

121:P2 将 x 的值增加到 11,而后 P1 将 x 设置为 x * x

110:P2 将 x 从 10 修改为 11 的动作出现在 P1 两次访问 x 的值之间,这两次访问是为了求值表达式 (* x x)

11: P2 访问 x,而后 P1 将 x 设置为 100,而后 P2 又设置 x

100:P1 访问 x (两次),而后 P2 将 x 设置为 11,而后 P1 又设置 x

可以用串行化的过程给此处的并发性增加一些限制,通过串行化组实现这种限制,假设构造串行化组的方式为调用 make-serializer。一个串行化组以一个过程为参数,它返回的串行化过程具有与原过程一样的行为方式。

(define x 10)
(define s (make-serializer))
(parallel-execute (s (lambda () (set! x (* x x))))
                  (s (lambda () (set! x (+ x 1)))))

通过此种方式只可能产生 x 的两种可能性 101 和 121,其他的几种可能性都被清除了,因为 P1 和 P2 的执行不会交错进行。

2.2.3 使用多重共享资源的复杂性

如果只存在一个共享资源(例如一个银行账户),串行化的使用问题是相对比较简单的。但是如果存在多项共享资源,并发程序设计就可能变得非常难以把握。

为了展示可能出现的一种困难,假设操作为交换两个账户的余额。

  • 首先访问每个账户,以确定其中的余额
  • 计算出两个余额之间的差额
  • 从一个账户里减去这一差额,然后将它存入另一个账户
(define (exchange account1 account2)
   (let ((difference (- (account1 `balance)
                        (account2 `balance))))
      ((account1 `withdraw) difference)
      ((account2 `deposit)  difference))
  • 只有一个进程试图做这种交换,这一过程能够工作得很好。
  • 涉及到多个进程操作这种交换,就会带来「多重共享资源的复杂性」问题

假定 Peter 和 Paul 都能访问 a1 、a2 和 a3 账户,在 Peter 要交换 a1 和 a2 时,正好 Paul 也并发地要求交换 a1 和 a3。

如果出现 Peter 算出 a1 和 a2 的余额差值,但是 Paul 却可能在 Peter 完成交换之前改变了 a1 的余额。

注:为了得到正确的行为,就必须重新设计 exchange 过程,让它在完成整个交换期间锁住对于账户的任何其他访问。

2.2.4 串行化实现

可以用一种更基本的称为「互斥元(mutex)」的同步机制来实现串行化。互斥元是一种对象,假定它提供两个操作:

  • 可以被获取(acquired)
  • 可以被释放(released)

一旦某个互斥元被获取,对于这个互斥元的任何其他获取操作都必须等到该互斥元被释放后进行操作。在实际实现使用中,给定一个过程 P,串行化组将返回一个过程,该过程获取相应互斥元,之后运行 P,最后释放互斥元,此种操作即可保证串行化性质。

2.2.5 死锁

在账户交互问题里还存在一个麻烦,假设 Peter 企图去交互账户 a1 和 a2,同时 Paul 并发地企图去交互 a2 和 a1。

  • 假定 Peter 获取了 a1 的互斥元,等待 a2 的互斥元
  • 假定 Paul 获取了 a2 的互斥元,等待 a1 的互斥元

此种情况下,就会进入「死锁」状态。

注:对于多种共享资源的并发访问的系统里,总是存在着死锁的危险。

避免死锁的一种方式,是首先给每个账户确定一个唯一的标识编号,使每个进程总是首先设法进入保护具有较低标识编号的账户过程。

注:此次笔者理解就是锁的粒度变的更大了,可以同时将 a1 和 a2 的账户锁定,在操作期。

2.2.6 并发性、时间和通信

在并发系统的程序设计中,关于以下两个问题,已经有清晰的描述:

  • 为什么需要去控制不同进程访问共享变量事件发生的顺序?
  • 如果使用串行化去控制并发?

但是,从一种更基本的观点来看,「共享状态」究竟意味着什么,这件事常常并不清楚。

「共享变量」的各个方面问题也出现在大型的分布式系统里。例如,设想一个分布式的银行系统,其中的各个分支银行维护着银行余额的局部值,并且周期性地将这些值与其他分支所维护的值相互比较。在这样的系统里,「账户余额」的值可能是不确定的。

假设 Peter 在他与 Paul 公用的一个账户里存入了一些钱,什么时候才能说账户的余额已经改变了:

  • 本地银行修改余额之后
  • 本地银行将余额同步之后

如果 Paul 从另一分支银行访问这个账户,如何在这一银行系统里对这种行为的「正确性」确定合理的约束?

在此处,能考虑的可能就是保持 Peter 和 Paul 的各自行为,以及保证刚刚完成同步时刻的账户「状态」正确性。

**这里的基本现象是不同进程之间的同步,建立起共享状态,或迫使进程之间通信所产生的事件按照某种特定的顺序运行 。**从本质上看,在并发控制中,任何时间概念都必然与通信有内在的密切联系。

注:在处理时间和状态时,在计算模型领域所遭遇的复杂性,事实上,可能就是物理世界中最基本的复杂性的一种反映。

3. 碎碎念

本来想要把第三章啃完的,但是这本书,看过的都知道有多晦涩难懂,那就慢一点吧,「走的慢一点,走的稳一点,走的久一点」

  • 不再走出舒适圈,一个人老去的标志,绝不是老成稳重,沉默寡言;而是不肯再尝试,不肯再容许自己置身不熟悉的境地,当你停止学习,固步自封,那么你已经向平庸迈进了一大步。
  • 沉溺短期快感,不再做长期投入,玩游戏、看小说,这是顺应人性,因为它们有及时反馈,你可以短期内获得快感。
  • 你现在状态不对,你就得改,你不能因为一件事情影响你太久,太耽误事了,成本过高,太不划算了。

注:你是什么时候发现时间竟然不知不觉的已经从指缝中流走的?

​ 在突然间意识到自己好像没有了刚刚工作时的那种拼搏向上的感觉时。

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

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

相关文章

Linux学习记录——구 进程概念的基础理解

文章目录一、操作系统概念理解二、进程的基本理解1、什么是进程&#xff1f;2、进程的属性1、指令查看进程2、目录查看进程3、进程与进程之间1、父子进程概念2、创建子进程---fork的基础使用方法3、fork原理的初级理解1、fork的操作2、fork如何看待代码和数据3、fork如何看待两…

【Docker概念和实践 2】虚拟机 ubuntu18上安装docker

一、说明 已经安装了N遍Docker了&#xff0c;逐步成了一套习惯&#xff0c;这里专门记录之&#xff1b;总之&#xff0c;安装前必须回答得问题是&#xff1a;何种操作系统、何种版本、是否虚拟机、云数据源等问题。一个环境如果装得好&#xff0c;就不需要重装&#xff0c;如果…

C语言刷题之摩尔投票法

目录 1.引入 2.摩尔投票算法 3.基本步骤 摩尔投票法分为两个阶段&#xff1a; 1.抵消阶段 2.检验阶段 4.代码实现 5.扩展沿伸 6.总结 1.引入 我们来看一个问题&#xff1a; 假设有一个无序数组长度为n&#xff0c;要求找出其中出现次数超过n/2的数&#xff0c;要求时间复…

vue3-环境搭建(docker版本)

序 大大小小项目经历无数&#xff0c;之前都是写的vue2的项目&#xff0c;因为项目需要&#xff0c;边学边用vue3&#xff0c;也算能转的开&#xff0c;但心里一直想系统的理顺一下vue3。 看了看极客时间&#xff0c;掘金小课&#xff0c;都没有能达到心里预期的“系统学习”…

免费内网穿透软件一步设置实现外网访问

在工作和生活中&#xff0c;有很多类似内网搭建服务器和外网连接内网的需求&#xff0c; 例如在任何地方都能访问自己家里的主机电脑笔记本上的应用&#xff0c;让出差外网和任何地方都能访问到公司内部局域网的服务器……这些需求我们可以统一用一个方案解决&#xff0c;那就是…

网络编程UDP+TCP

日升时奋斗&#xff0c;日落时自省 目录 1、网络编程基本概念 2、UDP数据报套接字编程 2.1、UDP相关API 2.1.1、DatagramSocket API 2.1.2、DatagramPacket API 2.2、UDP版本服务器 2.3、UDP版本客户端 2.4、UDP连接操作 2.5、翻译业务 2.6、总结 3、TCP流套接字编程 …

【项目实战】一文入门项目中Lombok的常用注解

一、Lombok介绍 1.1 Lombok是什么&#xff1f; 一个Java库&#xff0c;用于简化Java代码。 Lombok是一个非常神奇的 java 类库&#xff0c;会利用注解自动生成 java Bean 中烦人的 Getter、Setting&#xff0c;还能自动生成 logger、ToString、HashCode、Builder 等 java特色…

【GD32F427开发板试用】开发一款网络音乐播放器

本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动&#xff0c;更多开发板试用活动请关注极术社区网站。作者&#xff1a;守勤 资源介绍 非常荣幸能够参与到这次GD32F427开发板试用的活动中来&#xff0c;开发板的设计非常简洁&#xff0c;板载了一颗GD32F103C8T6和…

Python中的递归及案例演示

目录 一.什么是递归 二.案例 递归找文件 步骤 os模块中的三个方法 演示 最终代码 三.总结 一.什么是递归 递归在编程中是一种非常重要的算法 递归:即方法(函数)自己调用自己的一种特殊编程写法 如&#xff1a; 函数调用自己&#xff0c;即称之为递归调用。 二.案例 递…

C++ 引用! 他是坤坤也是鸡哥

&#x1f451;专栏内容&#xff1a;C学习笔记⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;日拱一卒&#xff0c;功不唐捐 目录一、前言二、引用1、引用的概念2、引用的声明3、引用的特性Ⅰ、 引用在定义时必须初始化Ⅱ、 一个变量可以有多个引用Ⅲ、引…

深度学习PyTorch 之 DNN-多分类

前面讲了深度学习&PyTorch 之 DNN-二分类&#xff0c;本节讲一下DNN多分类相关的内容&#xff0c;这里分三步进行演示 结构化数据 我们还是以iris数据集为例&#xff0c;因为这个与前面的流程完全一样&#xff0c;只有在模型定义时有些区别 损失函数不一样 二分类时用的损…

Pollard Rho算法

生日悖论 假设一年有nnn天&#xff0c;房间中有kkk人&#xff0c;每个人的生日在这nnn天中&#xff0c;服从均匀分布&#xff0c;两个人的生日相互独立 问至少要有多少人&#xff0c;才能使其中两个人生日相同的概率达到ppp 解&#xff1a;考虑k≤nk\le nk≤n 设kkk个人生日互…

Spring框架介绍及使用

文章目录1.概述1.1 Spring是什么1.2 Spring 的优势1.3 spring 的体系结构2. IoC 的概念和作用2.1 什么是程序的耦合2.2 IoC容器3. AOP的概念和作用超链接&#xff1a; Spring重点内容学习资料1.概述 1.1 Spring是什么 Spring 是分层的 Java SE/EE 应用 full-stack 轻量级开源…

使用docker-compose搭建Prometheus+Grafana监控系统

一、角色分配 Prometheus 采集数据Grafana 用于图表展示redis_exporter 用于收集redis的metricsnode-exporter 用于收集操作系统和硬件信息的metricscadvisor 用于收集docker的相关metrics 二、安装Docker 可以参考&#xff1a;https://ximeneschen.blog.csdn.net/article/d…

JVM调优实战:to-space exhausted Evacuation Failure

一次线上dubbo问题的定位&#xff0c;进行JVM调优实战。问题线上dubbo接口provider抛出异常&#xff1a;org.apache.dubbo.rpc.RpcException: Failfast invoke providers ... RandomLoadBalance select from all providers ... use dubbo version 2.7.16, but no luck to perfo…

vulnhub DC系列 DC-8

总结&#xff1a;exim4提权 目录 下载地址 漏洞分析 信息收集 网站爆破 后台webshell 提权 下载地址 DC-8.zip (Size: 379 MB)Download: http://www.five86.com/downloads/DC-8.zipDownload (Mirror): https://download.vulnhub.com/dc/DC-8.zip使用方法:解压后&#xff…

Cosmos 基础(二)-- Ignite CLI

官网 DOC GitHub 你的项目值得拥有自己的区块链。 Ignite使开发、增长和启动区块链项目比以往任何时候都更快。 Ignite CLI是一个一体化平台&#xff0c;可以在主权和安全的区块链上构建、启动和维护任何加密应用程序 Install Ignite 一、安装 你可以在基于web的Gitpod…

23种设计模式(七)——桥接模式【单一职责】

文章目录 意图什么时候使用桥接真实世界类比桥接模式的实现桥接模式的优缺点亦称:Bridge 意图 桥接模式是将抽象部分与实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interfce)模式。 什么时候使用桥接 1、如果一个…

详解MySQL数据库索引实现机制 - B树和B+树

详解MySQL数据库索引实现机制 - B树和B树1.索引的出现2.hash算法的缺点3.二叉排序树BST4.平衡二叉树AVL5.红黑树6.B树诞生了7.B树1.索引的出现 索引是一种用于快速查询和检索数据的数据结构&#xff0c;其本质可以看成是一种排序好的数据结构。 索引的作用就相当于书的目录。…

(Netty)Handler Pipeline

Handler & Pipeline ChannelHandler 用来处理 Channel 上的各种事件&#xff0c;分为入站、出站两种。所有 ChannelHandler 被连成一串&#xff0c;就是 Pipeline 入站处理器通常是 ChannelInboundHandlerAdapter 的子类&#xff0c;主要用来读取客户端数据&#xff0c;写…