7.3 关键区段产生的原因

关键区段(Critical Section)的最根本目的是确保系统数据结构的一致性。而数据的不一致性一般是由竞争修改引起的。所谓竞争修改,指的是多个实体(比如线程)试图同时修改该数据。下列讲述是操作系统中常见的竞争修改发生原因。

7.3.1 多个线程之间的竞争

假设在一个多线程单CPU的环境中,有一个记录所有核心对象数量的变量nKernelObject,这个变量可以被所有的线程共享。假设线程A创建了一个核心对象,并假设nKernelObject当前的值为100,这时候,该线程需要增加这个变量的值来反映这种情况(核心对象数量增加),于是可能会通过下列代码进行修改:

    nKernelObject+=1;

如果被编译成汇编语言,可能是下面这个样子:

mov eax,nKernelObject
inc eax
mov nKernelObject,eax

假设在上述第二条指令(inc eax)完成后,该线程的时间片用完,被临时阻塞,由于nKernelObject的值还没有被修改,所以仍然是100,但eax寄存器的值已经是101了。线程A被阻塞后,另外一个线程B被唤醒继续运行,而线程B同样创建了一个核心对象,也需要对nKernelObject进行递增。这时候,在线程B中会执行同样的代码。假设线程B在执行完上述最后一条指令(mov nKernelObject,eax)后,时间片用完,被阻塞。这时候,B修改了nKernelObject,使得nKernelObject变成了101,而不是原来的100。然后线程A又被唤醒,继续运行,由于线程A被挂起的时候,是刚刚执行完inc eax指令,所以线程A会继续执行mov nKernelObject,eax指令,这样原先存储在eax内的值(101)就覆盖了当前nKernelObject的值,因此当前nKernelObject的值仍然是101,于是不一致就产生了(正确的情况下nKernelObject应该是102)。

可以看出,产生上述问题的原因,就是线程A在修改nKernelObject变量时,被打断了。因此,为了解决这个问题,必须确保线程A在修改nKernelObject变量的时候,作为一个原子操作进行,不能被打断,即把上述修改nKernelObject的区段作为一个关键区段来对待。