The flaws of busy waiting:
- processor cycles are wasted reading locks
- can't guarantee fairness
- synchronizing variables look like ordinary variables
Starvation:
如果一个线程始终没有机会获取CPU time,就会starved to death,此时就依赖于fairness来grant a chance for all threads to execute
导致饥饿的三大常见原因:
- 拥有高优先级的线程长期的hog CPU
- 总是被其他的线程抢占进入权
- 总是被其他的线程抢占到对象
Semaphore: it provides a basic signaling mechanism and is used to implement mutual exclusion and conditional synchronization.(A special kind of shared variable that is manipulated only by two atomic operations P and V, the value of which can't be a non-negative integer)
- V, signal the occurrence of an event (increment the value)
- P, delay a process until an event has occurred (wait until the value of semaphore is positive then decrement the value)
Process P[i:1 to n] {
while(true) {
P(s);
CRITICAL SECTION
V(s)
}
}
Examples:
- Producer && Consumer
Message buf[1:n];
sem empty = 1; sem full = 0;
int rear = 1; int front = 1;
sem mutexP = 1; sem mutexC = 1;
Process Producer[i:1 to M] {
while(true) {
produce item;
P(empty);
P(mutexP);
add item to buf[rear];
rear = (rear + 1) % n;
V(mutexP)
V(full);
}
}
Process Consumer[j:1 to N] {
while(true) {
P(full);
P(mutexC)
remove item from buf[front];
front = (front + 1) % n;
V(mutexC)
V(empty);
consume;
}
}
- Dining philosophers
sem fork[5] = {1, 1, 1, 1, 1};
int N = 4;
Process Philosopher[i:1 to N] {
while(true) {
P(fork[i - 1]);
P(fork[(i+1) % N]);
eat;
V((fork[i - 1]);
V(fork[(i+1) % N);
think;
}
}
以上的代码可能导致死锁,所以可以只允许一次只有一个人就餐:
while(true) {
P(mutex;)
P(fork[i - 1]);
P(fork[(i+1) % N]);
eat;
V((fork[i - 1]);
V(fork[(i+1) % N);
V(mutex);
think;
}
但是这种方法效率又太低,所以:
while(true) {
if (i%2) {
P(fork[i - 1]);
P(fork[(i+1) % N]);
} else {
P(fork[(i+1) % N]);
P(fork[i - 1]);
}
eat;
V((fork[i - 1]);
V(fork[(i+1) % N);
think;
}
- The Readers and Writers
可以同时多个读者,任何时候只能有一个写程序
int nr = 0;
sem mutexR = 1;
sem rw = 1;
Process Reader[i:1 to M] {
while(true) {
P(mutexR);
nr = nr + 1;
if(nr == 1) P(rw);
V(mutexR);
read DB;
P(mutexR);
nr = nr - 1;
if(nr == 0) V(rw);
V(mutexR);
}
}
Process Writer[j:1 to N] {
while(true) {
P(rw);
write DB;
V(rw);
}
}
- Coordination
sem done = 0;
sem startAgain = 0;
Process Worker[i:1 to n] {
while(true) {
do the work;
V(done);
P(startAgain);
do the work again;
}
}
Process Coordinator {
while(true) {
for [i = 1 to 4]
P(done);
collect info;
for [i = 1 to 4]
V(startAgain);
}
}
Resource allocation:
sem mutex = 1;
sem b[1:N] = ([N] 0); List L, const int U;
Procedure request(int i, int u) {
p(mutex);
if(u < U) {
U-=u;
u(mutex);
}else{
put req(i, u) to L;
u(mutex);
p(b[i]);
}
}
Procedure release(int i, int u) {
p(mutex);
U+=u;
for each member(i, uuu) in List {
if(uuu < U){
U-=uuu;
remove(i, uuu) from L;
v(b[i]);
}
}
v(mutex);
}
Process:
while(true) {
compute u;
request(i, u);
use the resource;
release(i, u);
}
Reference:
1. the materials of Concurrent && Distributed Systems course