想说点啥
以前看书,总是走马观花,或者说就算不是走马观花,也是看了后面忘了前面,我估计是没做笔记的原因,所以,以后学习比较系统的东西时,多写点东西,也能对知识有个梳理。
最近在学java并发, 发现信号量这些东西不就是以前看csapp看过的东西吗,看来java只是做一层封装而已,因此决定复习一下csapp,顺便翻译翻译。
12.3 线程并发
一个进程里可以运行着多个线程。线程被内核调度。每个线程包含自己的线程上下文,包括:线程id,栈,栈指针,程序计数器,general-purpose寄存器和condition codes。(实在不知道怎么翻译)在一个进程里的所有线程共享虚拟地址空间,包括代码,数据,对,共享库和打开的文件。
因为线程上下文比进程上下文小得多,因此线程切换比进程切换快。
PThreads是C程序定义的一套操作线程的接口,包括创建、杀死线程等。
12.4 线程共享变量
一个线程无法读写另一个线程里的寄存器的值,但是却可以访问虚拟内存里的值。也就是说,寄存器不共享,虚拟内存共享。
栈的话,互相不设防,如果能拿到其他线程的栈上的指针,那么就可以读写。
12.5 使用信号量同步线程
对于线程i,指令L,U,S构成了一个临界区(也就是i++的三个指令), 这个临界区不应该与其他线程的临界区相交。也就是说,在临界区时,应该只有一个线程能够访问共享变量。这种现象叫做互斥。
在这张图中,我们定义了不安全区,穿过不安全区的轨迹就是不安全的。
不穿过不安全区的轨迹都将正确的更新变量。为了保证程序的正确与逆行,我们必须锁住线程让他们拥有安全的轨迹(也就是不穿过不安全区)。一个经典的方式是信号量。
12.5.2 信号量
信号量,s,一个全局的非负整数,只能被两种操作更改,P和V。
- p
- 如果s不是0,那么p减小s并立刻返回。
- 如果s是0,那么挂起线程直到s为非0,v操作会重启线程。在重启后,p减小s并将控制返回给调用者。
- v
- 将s+1
- 如果有线程处在被p阻塞的状态,那么v重启其中一个被阻塞的线程,这个线程将会完成p操作。
p中的检查和减一是不可分割的,如果s变为非0,s-1将会立刻发生。
v中的加载 & +1 & 存储信号量也是不可分割的。
注意v并没有定义到底重启哪个线程。
p和v保证了线程不可能进入一种信号量为负的状态。这也是我们即将用到的工具。
利用信号量达到互斥
定义一个信号量,值为1,因为信号量总是0或者1,提供了两者互斥的功能,因此二元信号量叫做互斥锁。
p操作是加锁。(因为其他线程想加锁会被阻塞)
v操作是解锁。(重启一个因想加锁而阻塞的线程)