并发:介绍

Monday, November 4, 2019

线程

多线程程序会有多个执行点,每个线程类似与独立的进程,但是有一点区别:多线程共享一个地址空间。

单个线程与进程非常相似,有个程序计数器,有自己的一组用于计算的寄存器。从一个线程切换到另一个线程,也会有类似上下文切换的操作,所以会有一个类似进程控制块(PCB)的线程控制块(TCB),但切换线程不需要更换地址空间,也就是不用切换页表。

在多线程程序中,每个线程独立运行,所以他们也可以拥有自己的栈,而不是单线程程序只有一个栈空间。

OS26.1

共享数据

线程的创建和运行有一个要点,它是根据调度程序的决定,来决定线程之间的运行顺序,而不是代码放置的先后位置。但有不管哪个先运行后运行,有一个重要的地方是需要关注的,那就是共享数据该如何处理。

比如,我们用多个线程,去更新同一个全局变量。

假设我们用两个线程,对一个初始数值为0的数字,自增100万次,那么我们预期的结果,自然是200万的,但是多线程运行的结果,未必会是200万,会出现比该数据少的情况,并且可能每次运行,结果都不一致。

不可控的调度

之所以会出现这种情况,是因为在运行过程中,发生了线程的切换,导致了竞态条件。在 A 线程进行完给变量加1操作后,即将赋值给变量的临界时刻,发生了线程切换,这样 A,B 线程都将赋值同一个数字给变量。

OS26.2

原子性

我们需要一个原子性的操作,来更新这个数据,要么完全更新成功,要么回退,不能出现中间状态。但是我们又不能让硬件直接提供这么一个操作。所以我们可以通过一些同步语句,使用硬件同步原语,加上操作系统的协助,以同步和受控的方式访问临界区代码。从而产生正确的结果。

等待另一个线程

上面我们说的是多线程访问同一个共享变量,但还有一种问题是我们要考虑的,就是多线程之间的等待,有些时候,某个线程需要等待另一个线程完成工作后,才开始执行。所以我们还需要研究唤醒/睡眠机制。

OS

超越物理内存:策略