第一章:介绍基本的背景知识——操作系统、线程、硬件
2、计算机硬件设备的三个核心部件:
1)中央处理器CPU
2)内存
3)I/O控制芯片
3、
1)开发工具与应用层是属于同一个层次的,因为他们都使用同一个接口,那就是——应用程序编程接口(API——Application Programming Interface)
2)而应用程序接口的提供者是运行库。
3)运行库使用操作系统提供的系统调用接口。
4)系统调用接口在视线中往往以软件中断的方式提供
4、操作系统的功能:
1)提供抽象接口。
2)管理硬件资源
5、多道程序——》分时系统——》多任务系统(CPU采用抢占式分配方式)
6、线程:
1)线程,有时候被称为轻量级进程(LWP),是程序执行流得最小单元。
2)一个标准的线程由线程ID、当前指针指令(PC)、寄存器集合、堆栈组成
3)通常,一个进程由一个到多个线程组成,各线程之间共享程序的内存空间(包括代码段、数据段、堆等)以及一些进程级的资源(如打开文件和信号)
7、二元信号量:
1)二元信号量是一种最简单的锁。它只有两种状态:占用和非占用。
2)它适合只能被唯一一个线程独占访问的资源,当二元信号处于非占用状态时,第一个试图获取该二元信号量的线程会获取该锁,并将二元信号量置为占用状态,以后其他的所有试图获取该二元信号量的线程将会被等待,直到该锁被释放。
8、volatile关键字:
1)阻止编译器为了提高速度将一个变量缓存到寄存器内而不写回。
2)阻止编译器调整volatila变量的指令顺序
3)volatile虽然能够阻止编译器调整顺序,但是无法阻止CPU动态调度换序。
8+、单例模式:Singleton
1)单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。
2)通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约资源。
3)如果希望在系统中的某个类的对象只能存在一个,单例模式是最好的解决方案。
9、一个类只能创建一个对象
当函数返回时,pInst总是指向一个有效地对象。而lock 和 unlock防止了多线程竞争的问题。
——双重if可以让lock的调用开销降低到最小。
但是:实际上这段代码是有问题的,问题来源于CPU的乱序执行。
1)C++里的new包含了两个步骤:分配内存 、 调用构造函数
2)所以pInst = new T也包含了三个步骤:1分配内存 、2在内存的位置上调用构造函数 、3 将内存的地址赋值给pInst。
3)在上面的三步中,2 3
的顺序是可以颠倒的。也就是说完全有可能出现这样的情况:pInst的值已经不是NULL了,但是对象还没有构造完。这时候,如果出现另外一个对GetInstance的并发调用,此时第一个if内的表达式pInst
== NULL 为false,所以这个调用会直接返回尚未构造完的对象的地址已提供给用户使用,那么程序就有可能崩溃。
11、内存屏障——barrier指令
1)内存屏障,也称内存栅栏、屏障指令,是一类同步屏障指令,使得CPU或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作。
2)大多数现代计算机为了提高性能而采取的乱序执行,这使得内存屏障成为必须。
3)语义上:内存屏障前的所有写操作都要写入内存;内存屏障之后的读操作都可以获得同步屏障之前的写操作的结果。
4)因此,对于敏感的程序块,写操作之后、读操作之前可以插入内存屏障。
12、许多体系结构的CPU都提供barrier指令,不过它们的名称各不相同,例如POWERPC提供的其中一条指令名叫lwaync。
我们可以这样保证线程安全: