多线程中的共享变量

00 前言

相比多进程并发,多线程一个好处就是:多线程很容易共享变量。然而,这种共享并不是很松,处理起来同样很棘手。为了更好地编写多线程程序,需要对所谓的共享和它们如何工作有个很清楚的了解。

01 线程存储器模型

线程有自己独立的线程上下文,包括线程 ID、栈、栈指针、程序计数器、条件码和通用目的寄存器的值。剩余的全部,也就是整个用户虚拟地址空间,包括代码、数据、堆以及共享库代码和数据区域,还要记得,线程也共享打开文件的文件集合。

从实际操作看,让一个线程去读写另一个线程中的寄存器是不可能的。因此,线程之间,寄存器从来不共享,而虚拟存储器总是共享的。

02 变量和存储器之间的映射关系

03 共享变量

共享是指,当且仅当变量实例被一个以上的线程引用。

对等线程内部,本地静态变量可以共享,本地自动变量不可以。同时,主函数之外的全局变量可以共享,主函数内,主线程的本地自动变量也是可以共享的。

04 用信号量同步线程

共享变量是十分方便的,但同时也引入了同步错误

例如,在一个例程中,有一个循环,循环次数确定为 10000,循环的内容很简单,只是在每次循环时,将共享的全局变量实例的值增加 1 。我们通过这个例程建立两个对等线程,分别执行。等待它们各自运行结束时,检查全局变量的值,会发现其增加量并不是 10000 X 2 。

这里的问题在于,C 语言里的循环语句中的一个增值操作,在汇编语言中,实际上有一系列步骤。所谓并发执行,在单核处理器上,往往是两个循环体的汇编层面上的某种合序,可以认为是一种交叉。交叉之后,就不再一定是我们想要的并行,因为粒度完全不一致了,已经混合在一起。混合之后的顺序,有的对,有的不对。

再加上:一般而言,没有办法干预操作系统调度线程并发时的执行顺序一定是符合自己意图的那一个

而处理这个问题,需要进度图:将 n 个并发线程的执行模型化为一条 n 维笛卡尔空间中的轨迹线,构成轨迹的坐标是每个线程下的指令状态序列

每个坐标就是一个状态,状态之间的变化,代表程序的执行,称为转换

合法的转换是有限制的:

那问题来了,怎么控制轨迹线呢?通过信号量:

待续。。。


不知是该恭喜,还是该怎样,总之阅读到该文的,你是第 人。每一次刷新,都是不同的自己。