一直对于条件变量的原理不是特别清楚,今天google了下,搜到了斯坦福大学os的课件,算是看明白了。
Locks
- 锁:在任意时间一个对象只能被一个线程拥有。锁有以下基础操作:
- acquire:使得锁被当前线程拥有;如果其他线程已经拥有锁,要等待这个锁被释放。锁特别包含一个等待线程队列信息的堆栈。
- release:释放锁(必须被持有的当前线程释放)
- 比如
struct lock l; ... lock_acquire(&l); if (milk == 0) { buy_milk(); } lock_release(&l);
- 更复杂的例子是生产者消费者:
- 生产者向buffer加字幕
- 消费者从buffer移除字母
- 字母将被以放入的顺序被移除
- 比如:
char buffer[SIZE]; int count = 0, putIndex = 0, getIndex = 0; struct lock l; lock_init(&l); void put(char c) { lock_acquire(&l); count++; buffer[putIndex] = c; putIndex++; if (putIndex == SIZE) { putIndex = 0; } lock_release(&l); } char get() { char c; lock_acquire(&l); count--; c = buffer[getIndex]; getIndex++; if (getIndex == SIZE) { getIndex = 0; } lock_release(&l); return c; }
- Version 2 (handle empty/full cases):
char buffer[SIZE]; int count = 0, putIndex = 0, getIndex = 0; struct lock l; lock_init(&l); void put(char c) { lock_acquire(&l); while (count == SIZE) { lock_release(&l); lock_acquire(&l); } count++; buffer[putIndex] = c; putIndex++; if (putIndex == SIZE) { putIndex = 0; } lock_release(&l); } char get() { char c; lock_acquire(&l); while (count == 0) { lock_release(&l); lock_acquire(&l); } count--; c = buffer[getIndex]; getIndex++; if (getIndex == SIZE) { getIndex = 0; } lock_release(&l); return c; }
条件变量:
- 同步机制需要的不止是互相掣肘;也需要一种方式去等待另外一线程做一些事情(比如 等待一个字母加进buffer)
- Condition variables: used to wait for a particular condition to become true (e.g. characters in buffer).
- 条件变量:用来等待一个特定的条件变为true。
- wait(condition, lock): 释放锁,休眠当前线程知道条件符合被唤醒;当线程被唤醒,再次获得锁。
- signal(condition, lock): i如果有任何线程在等待这个条件变量,唤醒其中一个。调用者必须持有锁,而且必须与等待条件使用的是同一把锁。
- broadcast(condition, lock): 与信号一样,当然是唤醒所有等待线程。
- Note: after signal, signaling thread keeps lock, waking thread goes on the queue waiting for the lock.
- 注意:signal后,signaling线程继续持有锁,唤醒线程继续等待在锁上。
- 注意:当一个线程被条件唤醒并不保证条件符合。
- Producer/Consumer, version 3 (with condition variables):
char buffer[SIZE]; int count = 0, putIndex = 0, getIndex = 0; struct lock l; struct condition dataAvailable; struct condition spaceAvailable; lock_init(&l); cond_init(&dataAvailable); cond_init(&spaceAvailable); void put(char c) { lock_acquire(&l); while (count == SIZE) { cond_wait(&spaceAvailable, &l); } count++; buffer[putIndex] = c; putIndex++; if (putIndex == SIZE) { putIndex = 0; } cond_signal(&dataAvailable, &l); lock_release(&l); } char get() { char c; lock_acquire(&l); while (count == 0) { cond_wait(&dataAvailable, &l); } count--; c = buffer[getIndex]; getIndex++; if (getIndex == SIZE) { getIndex = 0; } cond_signal(&spaceAvailable, &l); lock_release(&l); return c; }
Monitors
- 当锁和条件变量一起用,结果被叫做监察者
- A collection of procedures manipulating a shared data structure.
- One lock that must be held whenever accessing the shared data (typically each procedure acquires the lock at the very beginning and releases the lock before returning).
- One or more condition variables used for waiting.
- There are other synchronization mechanisms besides locks and condition variables. Be sure to read about semaphores in the book or in the Pintos documentation.