ticket锁利用了一种硬件原语:Fetch-And-Add(获取并增加)指令。
int FetchAndAdd(int *ptr) {
int old = *ptr;
*ptr = old + 1;
return old;
}
只是简单的返回旧值,然后加1.
实现锁的代码如下
int FetchAndAdd(int *ptr) {
int old = *ptr;
*ptr = old + 1;
return old;
}
typedef struct lock_t {
int ticket;
int turn;
} lock_t;
void lock_init(lock_t *lock) {
lock->ticket = 0;
lock->turn = 0;
}
void lock(lock_t *lock) {
int myturn = FetchAndAdd(&lock->ticket);
while (lock->turn != myturn); // spin
}
void unlock(lock_t *lock) {
FetchAndAdd(&lock->turn);
}
如果线程希望获取锁,首先对一个 ticket 值执行一个原子的获取并相加指令。这个值作为该
线程的“turn”(顺位,即 myturn)。根据全局共享的 lock->turn 变量,当某一个线程的(myturn
== turn)时,则轮到这个线程进入临界区。unlock 则是增加 turn,从而下一个等待线程可以
进入临界区。
本方法能够保证所有线程都能抢到锁。只要一个线程获得了 ticket值,它最终会被调度。