Locks
为了解决在执行一系列指令时中间发生中断事情,引入了lock。
1. Locks: The Basic Idea
使用lock
,关键部分为balance = balance + 1
。
2. Pthread Locks
传递了一个变量来锁定和解锁,因为可能使用不用的锁来保护不同的变量(提高并发性);还可以保护不同的数据和具有不同锁的数据结构,从而允许更多线程进入一次锁定代码。
3. Building A Lock
构造一个Lock,我们需要OS和硬件支持。
4. Evaluating Locks
评估一个锁是否有效,效率好坏的指标:
- mutual exclusion(一个锁的基本任务)
- fairness(线程获得锁的机会平等)
- performance(使用锁的时间消耗)
5. Controlling Interrupts
防止关键部分被打断的一种早期的解决方案就是在关键部分执行时停止中断程序。代码如下所示:
如果我们运行在一个单核处理器系统,这是非常简单的。
但是,也有很多缺点,如下所示:
- 线程调用必须执行优先操作,而且要相信其不会被乱用
- 该方法不能用于多处理器系统
- 长时间关闭中断会导致中断丢失
- 该方法效率可能低下
6. A Failed Attempt: Just Using Loads/Stores
上述代码也是非常简单能看懂,但是有两处问题:
- correctness
- performance
首先看一下正确性,如果有如下代码:
上述的情况就破坏了锁的基本需求:mutual exclusion
第二个性能问题就是无止尽的检查flag的值,浪费很多时间周期(自旋锁)。
7. Building Working Spin Locks with Test-And-Set
int TestAndSet(int *old_ptr, int new) {
int old = *old_ptr; // fetch old value at old_ptr
*old_ptr = new; // store 'new' into old_ptr
return old;
}
typedef struct __lock_t {
int flag;
} lock_t;
void init(lock_t *lock) {
// 0: lock is available, 1: lock is held
lock->flag = 0;
}
void lock(lock_t *lock) {
while (TestAndSet(&lock->flag, 1) == 1)
; // spin-wait(do nothing)
}
void unlock(lock_t *lock) {
lock->flag = 0;
}
这种方法比上一种更好,但是还是有spin lcok(自旋锁)的问题存在。
8. Evaluating Spin Locks
自旋锁保证了mutual exclusion,但是没有保证fairness。
对于performance,单处理器性能极差,但是多处理器(线程数理等于CPU数量)性能还不错。
9. Compare-And-Swap
能够取代lock()
程序如下所示:
10. Load-Linked and Store-Conditional
上述关于lock()
能更进一步简洁:
void lock(lock_t *lock) {
while (LoadLinked(&lock->flag) || !StoreConditional(&lock->flag, 1))
; // spin
}
11. Fetch-And-Add
int FetchAndAdd(int *ptr) {
int old = *ptr;
*ptr = old + 1;
return old;
}
使用fetch-and-add构造ticket lock:
12. Too Much Spinning: What Now?
上述的方法对于单处理器多线程性能是非常低下的:N个线程竞争一个锁,N - 1个时间片会浪费掉。
故为了提高性能,我们要避免spinning。
13. A Simple Approach: Just Yield, Baby
第一种解决上述问题的方法非常简单,代码如下所示:
相比于spinning,性能有了改进,但是时间开销还是非常大(上下文切换的成本可能很高)。
更糟糕的是,我们根本没有解决starvation。一个线程可能陷入无限的让出CPU循环状态,而其他线程则反复进入和退出关键部分(critical section)。
14. Using Queues: Sleeping Instead Of Spinning
该方法可能出现的问题:wakeup/waiting race。
为了解决该问题:对lock()
函数做了一些修改。
最后还要谈及一点:对于不同的OS有不同的硬件支持。