第一章—介绍

和所有其他并发书籍一样这本书开头也是列举了自增运算符在多并发场景下的可能出现的错误。

原子性:

对于自增运算,现代的任何一个计算机都会把这个操作实现成三步:从内存加载数据到寄存器,增加寄存器中的数据,然后再把增加之后的数据写入到内存。

如果我们的计数器初始值是0的话,在正常情况下,当两个线程执行完成之后,我们会得到2。但是如果一个现在在另一个线程执行第三步之前率先执行了第一步的话,两个线程都会往内存存入1,这样另一个线程的增加就会被丢失掉(不会得到2)。

同步是一门用来防止程序进行错误交叉执行路径的艺术。在一个分布式系统中,同步被归类为通讯交流:如果T2线程从T1线程那里收到了一个消息,那么在所有可能的交叉执行路径中,T1中所有在发送消息之前发生的事件都会在T2收到消息之前发生。

有一部分语言和系统保证在同一时刻只有一个线程在执行,并且线程上下文的切换会发生在实现定义好的点上。

事实证明,在现实世界编程的模式中,基本上所有的同步模式都可以被认为是atomicity or condition synchronization中的一个实例。原子性保证了,一个指令执行序列在执行过程中是不会出现其他执行序列的。所有的交叉执行都是假设底层的机器指令是原子执行的。条件同步保证特定的操作只有在必要的前提条件是true的情况下才会出现。通常,这个前提条件都是其他线程某些操作的完成。

实现原子性最简单的方式就是强制这些线程每次只有一个线程来执行他们的操作。这个策略被称作互斥性。那些被互斥性执行的操作集合称作临界区。

如果在获取个字对方的资源之前,线程1获得lock2,线程2获得lock3。这俩会无限制的等待。一个最简单的解决方法是,线程总是优先获取数字最小的锁。这样的结果是,线程2在没有获得lock2的情况下是不会获取lock3的。但是在大多数情况下,维持这样一个静态的序列是复杂的。另一个可选的原子性实现方案是事务内存。从程序员的视角来看,细粒度的锁定是用更小的临界区来为大型的,复杂的操作实现原子性的一种手段。保证实现的正确性完全是程序员的责任。事务内存提高了抽象程度,这样让程序员可以把这个责任委托给底层的系统去实现。

不管原子性是通过粗粒度的锁,程序员管理的细粒度的锁,或者某种个是的事务内存,所有的意图都是原子的区域都是不可分割的。换种说法就是程序的任何可行的执行路径——他的机器指令的所有可能交叉执行的情况——必须和那些按照时间执行的原子操作是不可区分的,这些原子操作都不会有其他指令在他里面执行。在第三章,有很多方法可以规范这些要求,更显著的可线性化和可串行化的几个变种。

条件同步:

在同步性研究中,一个并发队列有些时候会被称为划定边界的buffer,他是既包括同步又包括原子性的一个权威的例子。像我们上面介绍的await condition,bounded buffer中的条件可以放在临界区的开头。在更复杂的情况下,一个线程在临界区内不知道他将要等待的条件之前,执行了一个重要的工作。为了让条件成立,另一个线程可能会修改同一个数据结构,一个处于临界区中间的wait操作可能会打破整个临界区的原子性。在第七章我们会看到只支持简单的在临界区等待的同步机制,其他的同步机制允许条件出现在临界区的任意地方。

在临界区之外,条件同步也是很有用的,比如同步栏栅(synchronization barrier),他保证了只有所有的线程都执行了最后一步,其他的线程才可以离开。

假设原子性的实现比条件同步要简单是很吸引人的。毕竟,原子性可以被看作是条件同步的一个子类,一个线程一直等到临界区没有其他线程的时候才会执行。这么想带来的问题就是条件的范围。为了方便,我们把条件暂且考虑成变量的值而不是线程的状态。看到这里,原子性要求所有的线程要达成一致。

自旋和阻塞:

         

像同步模式会分为两个阵营,他们的实现也会分为两个阵营:他们都采用自旋或者阻塞。自旋是一个简单的例子。比如条件同步,他采用一个一般的循环。

实现互斥的最简单的方法就是使用一个硬件指令TAS,这个硬件指令在大多数现代计算机上都会有,设置一个布尔值为true,并且返回之前的值。用TAS可以实现简单的自旋锁。

忙等待的明显缺点就是它浪费了cpu的执行周期。在一个多应用程序系统,经常会使用阻塞——让处理器去执行其他的可执行的线程。之前的线程也许会不久之后又会执行。

负责选择哪个线程去执行的软件叫做调度器。在很多系统中,调度出现在两个不同的层次上。在操作系统层面上,一个内核调度器在处理器核上实现threads;在用户运行时层面上,一个用户调度器在内核调度器上实现用户级别的threads。不管在哪个层面上,实现threads的代码一般都是以库文件接口的行尸出现,包括一整套调用子程序。另外,内核或者应用程序所使用的语言可能会提供特定的线程管理和同步,这取决于编译器的实现。

在不同层次上的调度器有特定的功能。内核级别的调度器,让不同应用程序之间不可以互相访问对方的内存地址来保护应用程序。用户级别的调度器,可能会用栈式的实现方式。更大程度上说,内核或者用户级别的调度器,有着相似的内部结构,并且在不同层次上,轮训和阻塞都会很有用。

阻塞不用不停的去查看条件和锁的状态,但是他会在来回切换程序的时候有性能花费。如果线程等待的平均时间小上下文切换时间的两倍,则可以优先考虑轮训。当每个cpu核上之后一个线程在执行的时候,轮训也是不错的选择,这通常是发生生在嵌入式或者高性能的系统中。最终我们会在第七章中发现,阻塞(基于调度的同步)一定是基于轮训实现的,因为调度器使用的数据结构本身也需要同步。

时间: 2024-08-01 00:18:51

第一章—介绍的相关文章

perl进阶中文版 第一章 介绍

翻译尊重原版,以自己的方式最大化地阐述原版内容. 更多内容请访问www.from0701.com.每周更新两章. 欢迎来到perl学习的下一阶段.你来这儿的理由可能是你想编写100行以上的代码或者仅仅是你的老板要求你这么做. 我们的<perl语言入门>如此伟大是因为他介绍了如何用perl编写中小型程序(在我们看来,这是perl最普遍的用途).但是为了不让我们的“小骆驼书”太厚重,我们有意并且小心地移除了很多内容. 接下来,我们将用和小骆驼书一样的风格去继续我们的故事.这儿包含了编写100-10

python 第一章 介绍-1.python特点.

一.Python特点 开源免费 脚本语言,解析执行 跨平台 高级语言,面向对象,可扩展,可移植性用于在不同的平台(因为Python是用C写的,又由于C的可移植性) 内存管理器在Python中,由于内存管理是由Python解释器负责的,所以开发人员就可以从内存事物中解放出来,全神关注于直接目标.解释性语言,不需要编译,连接成可执行的exe文件代码 Google后台就是用Python写的,现在国内大型软件公司的后台基本 它的特点如下: ------------------------- 1.高级编程

UNP --- 第一章 介绍

一个简单的时间获取客户端程序 1 #include "unp.h" 2 3 int main(int argc, char **argv) 4 { 5 int sockfd, n; 6 char recvline[MAXLINE + 1]; 7 struct sockaddr_in servaddr; 8 9 if (argc != 2) 10 { 11 err_quit("usage: a.out <IPaddress>"); 12 } 13 if (

第一章 Android系统的编译和移植实例

第一章 Android系统的编译和移植实例 这一章节主要介绍了Android系统的编译和移植技术,作为建立在Linux内核的基础上的Android操作系统,它的编译和移植不论在过程还是技术方面都和嵌入式Linux非常相似. 首先要准备一套可以正常运行Linux系统的一套开发版,需要在其移植Android系统,并能够正常运行. 移植的主要过程为: 1.下载Android Linux 内核 2.安装交叉工具链 3.移植Android Linux 内核支持的平台 4.安装Android SDK 5.获

ASM学习笔记--ASM 4 user guide 第一章翻译

第一章 介绍 1.1动机 程序分析.生成和转换是非常有用的技术,它具有以下的应用场景: l  程序分析(包括从简单的综合性分析到一个全面的语义分析)可以被用来寻找潜在的bug,发现未使用的代码,进行工程代码的逆向. l  程序生成被用在编译器当中.这包括传统的编译器,也包括为分布式编程使用的stub或skeleton 编译器,即时编译器等 l  程序转换可以被用来优化或者混淆程序,为程序插入debugging或者性能检测代码,方便面向对象编程等. 所有这些技术可以被用到任意的编程语言,但是难易程

Android深度探索读后感 第一章

第一章 介绍Android驱动开发和移植技术 一.Android的四层系统架构: Android的系统架构分为4层,分别为:Linux内核,C/C++代码库,Android SDK API,应用程序. Linux内核:Linux内核包括Linux的驱动以及内存管理,进程管理,电源管理等程序. Linux的驱动:驱动就是驱使硬件设备行动.驱动是直接与底层硬件直接打交道,按照一定的工作方式,读写设备的寄存器,具有完成设备的轮询.中断处理.DMA通信,进行物理内存向虚拟内存的映射等功能,最终让通信设备

Delphi基本之pascal语法(第一章.pascal介绍)

第一章.pascal介绍一.pascal的基本语法单位 1.基本符号 A——Z:a——z:0——9:+,-,*,/,=,<>,<=,>=,<,>,(,),[,],{,},:=,,,:,.,:,..,',^ 注意:pascal语言除了能使用以上规定的基本符号外,不得使用任何其它符号. 2.保留字 AND,ARRAY,BEGIN,CASE,CONST,DIV,DO,DIWNTO,ELSE,END,FILE,FOR,FUNTION,GOTO,IF,LABEL,MOD,NIL,

Camel In Action 阅读笔记 第一章 认识Camel 1.1 Camel 介绍

1.1 Camel 介绍 Camel 是一个为了您的项目集成变得高效有趣的集成框架,Camel 项目在2007年初开始的,相对来说它还比较年轻,但它已然是一个非常成熟的开源项目,它所使用的是Apache 2开源License, 其背后的社区非常强大. Camel主要目的就是简化集成,当您用心读完本书以后,您会很感谢Camel并把它做为您的一个必需技能. Apache Camel这个项目之所以命名为Camel的主要原因是因为简单好记,有一个小道说法是项目创建者之一在吸了一个叫"骆驼"牌的

The Book of CSS3 中文版 第一章:介绍CSS3

在这一章,为了展示本书所用的代码约定我会介绍一些新的CSS3属性,但在此之前我想简要说明下CSS3的历史.很想然你不需要为了使用CSS3去了解它的历史,但是我认为有一些关于CSS3当前状态的背景是很重要的. CSS3是一个在变化的规范.规范的一部分被认为是稳定的并且在现代浏览器中得到了很好的实现:规范的另一部分应当作实验性的并且被部分地不同程度的实现:还有一部分则仍然是理论上的提议,并且没有得到任何实现.一些浏览器创建了它们自己的CSS属性,这些属性不属于任何CSS3规范也许将来也不会添加进去.