第四章 分布式与并行计算
4.1 引言
这一章,我们转到协调多个计算机和处理器的问题。
- 分布式系统:多个互相连接的独立计算机,需要沟通来完成任务。
- 并行计算:多个处理器并行工作来使任务完成的更快。
4.2 分布式系统
4.2.1 C/S架构
C/S架构是一种中心来源分发服务的模式,只有单个服务端提供服务,多台客户端和服务端通信来消耗他的产出。
- 是一种函数式抽象,服务端&客户端彼此不需要知道对方实现的细节。
- 一个机器上的系统也可以有C/S架构。
- 服务端是单点故障。
- 服务端也可能是性能瓶颈,当客户端很多时,资源就会变得稀缺。
4.2.2 P2P架构
P2P系统中,系统的所有组件都对分布式计算贡献了一定的处理能力和内存。
- 对等者(peer)需要能够和其他人可靠的通信,也即需要组织良好的网络结构。
- 一些P2P系统中有一些组件负责维护网路健康的任务,这种并不是纯粹的P2P系统,因为它们具备不同类型的组件。
- P2P系统最常见的应用就是数据传送和储存。
4.2.3 模块化
模块化是一个概念,系统的组件对于其他组件来说应该是个黑盒。组件如何实现行为不重要,只要它提供了一个接口:规定了输入应该产生什么输出。 分布式系统中,我们将接口的概念从对象和消息扩展为整个程序。接口在真实世界中也普遍存在,例如遥控器、手机。 模块化给予系统许多好处:
- 易于理解
- 易于修改和扩展
- 出现错误时,只需更换有错误的模块
- 易于定位bug或故障
4.2.4 消息传递
- 发送者
- 接收者
- 内容
- 内容可以是数据、信号甚至指令
- 在较高层次上理解,内容可以是复杂的数据结构
- 在较低的层次上,内容只是0/1比特流。
- 消息协议
- 所以在网络上传输时,为了避免编码问题,需要消息协议来指定编码和解码的格式。
- 消息协议不是特定的程序或软甲库,它可以由不同的语言编写。
4.3 并行计算
- 为了合作,多个处理器需要能够彼此共享信息。
- 多个进程之间共享状态,可能会出现单一进程环境没有的问题。
- 并行计算的正确性:
- 锁
- 一个进程不拥有特定的锁,就无法访问相应的变量。
- 死锁,多个进程同时持有彼此需要的锁,导致谁都无法继续运行下去,互相等待。
- 信号量
- 维持有限资源访问的信号。
- 允许某个数量的访问,超过该数量,其余的进程就不允许访问。
- 条件变量
- 进程主动暂停自己,直至其他进程/OS告知该进程可以继续。
第五章 序列与协程
5.1 引言
有序数据是编程中常用到的一类数据,有很多序列接口可以有效地帮助我们处理序列。但序列有以下两个限制:
- 序列越长,占据的内存空间就越大。
- 序列只能表示已知且长度有限的数据集。
这一章我们使用新的构造方式来处理有序数据,它容纳未知或无限长度的集合,而仅仅使用有限的内存。我们也会将这些工具用于协程来有效、模块化的构建数据处理流水线。
5.2 隐式序列
序列可以用一种程序结构来表示,这种结构可以提供序列中所有元素的按序访问(链式存取),而不用事先把所有的元素计算出来并储存。
迭代器是提供底层有序数据集的有序访问的对象,他有两个组成部分:
- 获取序列中下一个元素的机制
- 表示序列已达到末尾的机制
- 迭代器通过
raise StopIteration 异常来表示已达末尾
迭代器类通过__iter__() 方法来返回迭代器对象,在python中,满足了上述需求的迭代器可以直接在for...in 循环中使用。 迭代器的__next__() 方法在复杂的序列的情况下,很难在计算中节省空间。生成器允许我们定义更复杂的迭代。
- 生成器是由生成器函数返回的迭代器。
- 生成器函数是一种特殊的函数
- 没有
return 语句。 - 通过
yield 语句来返回序列中的元素。 - 每次调用
__next__() 方法时,函数执行到下一个yield 语句。
python中,迭代只会遍历一次底层序列的元素,如果之后再次掉用,则会产生StopIteration 异常。 如果一个迭代器对象在每次调用__iter__() 时返回了新的迭代器实例,那么他就可以被迭代多次。
流提供了一种隐式表示有序数据的最终方式。
- 流(
Stream )是一种惰性计算的递归列表。 Stream 实例可以响应对其第一个元素和剩余部分的获取请求。
- 流会储存计算剩余部分的函数。
- 操作序列的函数
map 和filter 同样可以应用于流。但他们的实现必须修改来惰性调用他们的参数函数。 - 就像递归列表提供了序列抽象的简单实现,流提供了简单、函数式的递归数据结构,它通过高阶函数实现了惰性求值。
5.3 协程
协程是一种针对有序数据任务的处理方式,他会计算复杂计算中的一小步,但是这里不想辅助函数,没有主函数来协调结果。协程们自发的链接到一起来组成流水线。 Python的生成器函数可以是使用(yield) 来消费一个值,生成器对象上有两个额外的方法:send() 和close() 创建了一个模型使对象可以消耗或者产出值。定义了这些对象的生成器函数叫做协程。 我们可以将使用send() 和(yield) 的函数连接到一起来完成复杂的行为。
- 生产者使用
send() 创建序列中的物品。 - 过滤器使用
(yield) 来消耗物品,并将结果使用send() 发送给下一个步骤。 - 消费者使用
(yield) 来消耗物品,但是不发送。 - 当过滤器或者消费者被构建时,必须调用它的
__next__() 方法开始执行。 - 生产者或过滤器并不受限于唯一的下游,可以有多个协程作为他的下游。同时使用
send() 向他们发送数据。
|