同步问题诞生的最本质的原因:In fact, a process may be interrupted at any point in its instruction stream, and processing core may be assigned to execute instructions of another process.总之一句话,关于共享对象的更改操作并非原子操作,如假设两个进程对于共享对象counter进行不同的操作:
一般地,counter++将被翻译成低级别的原子操作序列
而counter–将被翻译成
考虑到现今操作系统为了提供更好的实时响应特性,抢占式内核是主流设计,故而上面两个原子操作序列的执行顺序是可以互相交叉的,故而最后counter的结果是不确定的。这种有多个进程操作同一数据对象,并且结果依赖于此次具体执行顺序的情况被称作race condition(竞争状况)。
既然提供了硬件加锁的可能,操作系统必然也是为给出一系列方便程序员使用的锁机制。The hardware-based solutions to the critical-section problem using atomical function eg. test_and_set() are complicated as well as generally inaccessible to application programmers. Instead, operating-systems designers build software tools to solve the critical-section problem. Mutex便是操作系统提供的诸多锁机制中的最简单的一个(Mutex是mutual exclusion的合并)。
锁机制的经典使用机制
Mutex是在上面介绍的原子操作test_and_set()等atom lock基础上封装而来的,可以实现锁的互斥性,但是Mutex最大的问题在于它存在“空等”情况,如果process_i进入了临界区,其余进程必须一直调用acquire()直到分配成功。正是因为“空等”的存在,故而Mutex这类锁也被称为自旋锁。In fact, this type of mutex lock is also called a spinlock because the process “spins” while waiting for the lock to become available.
但也正是因为自旋空等的存在,自旋锁生效期间,等待进程没有产生进程上下文内容切换(no context switch is required),因为上下文内容切换也是要花费时间的(a context switch may take considerable time)。所以如果单一线程占用共享资源的时间较短时,采用自旋锁是合理的,其实就是比较盲等时间和让进程切换上下文执行其他内容带来的收益。
旗语semaphores分为计数counting和binary两种,前者取值范围是(-∞,+∞),后者只能在0和1间取值,binary semaphore相当于Mutex Locks。
Counting semaphores相当于为某一资源提供次数有限的访问权限,类似于线程池thread pool为外部服务请求进行处理的场景,需要用counting semaphores管理每时刻线程池空闲服务线程数目。
上面我们说道Mutex虽然简单,但是存在等待进程盲等的计算资源浪费情况。在Semaphores中,相比于Mutex存在盲等,Semaphores有着更经济的操作方式,一旦进程检测到Semaphores Value小于等于0,则当前进程自我阻断,进入一个和Semaphores绑定的等待队列中,该进程的状态被置为waiting状态,控制权交给CPU调度中心,由其选择下一进程执行(The block operation places a process into a waiting queue associated with the semaphore, and the state of the process is switched to the waiting state. The control is transferred to the CPU scheduler, which selects another process to execute.)而进入休眠状态的进程需wakeup()指令激活,然后进入ready queue等待获取资源。
其中block()操作将会中止它的宿主进程,而wakeup(P)则会激活进程P,这两个功能函数均是由操作系统实现的(基于中断机制实现)。而Semaphores配备的等待队列采用的排队策略,最简单的当然是FIFO,但是配备其他策略也是可以的,并不影响semaphore的正常使用。
简单一句话,如果critical section的操作指令并不多,完全可以借助Mutex或者Binary Semaphores来实现锁机制,没必要使用Semaphores的这种配备等待队列的机制来实计算时间节约,但如果临界区内操作复杂(使用进程占用时间很长),这种情况下应该使用Semaphores配备等待队列机制,以集中计算资源给当前正在临界区中的进程使用。
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!