您的位置:首页 > 其它

操作系统 线程同步 基础知识 (一)

2014-03-26 22:12 197 查看
6.2 临界区问题

临界区问题的解答必须满足以下三项要求:

1. 互斥. 不能有两个线程同时在临界区内执行

2. 前进. 当临界区为空, 而一个线程希望进入临界区时, 该线程进入临界区

3. 有限等待. 一个线程从申请进入临界区到真正进入临界区这段时间不能无限长.

6.3 Peterson 算法

一种软件的方法解决死锁问题, 但也并不能总是解决死锁问题

定义两个变量, 分别为 flag[0/1], turn. flag 表示哪个线程有进入临界区的意愿, turn 表示哪个线程

while(true) {
flag[i] = true;
turn = j;

while(flag[j] && turn == j); // let j run first
// cirtical section

flag[i] = false;
// remainer section
}


证明满足上面 3 个要求

1. 互斥. 假设两个线程同时进入临界区, 那么 flag[i] == flag[j] == 1. 当 turn = j 时, 线程 i 在自旋, 不可能进去.

2. 前进. 当线程 j 没有意愿进入临界区时, flag[j] = false; 当 i 有意愿进去时, 直接就进入了.

3. 有限等待. 首先申请临界区的线程进入临界区, 一个线程至多等待另一个线程在临界区执行一次, 满足有限等待.

6.4 硬件同步.

使用原子函数 setAndSwap()

void getAndSet(bool var) {
swap(lock, var);
}


上面的代码核心是 getAndSet 必须时原子操作. 当 lock 为 false 时, 线程上锁进入临界区. 当 lock 为 true 时, 那么 getAndSet 无限循环, 直到 lock 为 false;

6.5 信号量与死锁

acquire() {
value --;
if(value < 0) {
add this process to list
block;
}
}

release() {
value ++;
if(value <= 0) {
remove a process P from list
wakeup(P);
}
}


当 value 为负时, value 的绝对值对应被阻塞线程的数目.

死锁

S.acquire();         Q.acquire();
Q.acquire();         S.acquire();

...			...

S.release();         Q.release();
Q.release();         S.release();


上面代码中, P0 执行 S.acquire(), P1 执行 Q.acquire, 然后 P0 执行 Q.acquire, 最后 P1 S.acquire.

然后... 就死锁了

6.6 经典同步问题

1. 有限缓冲区问题

生成者通过 insert 函数向缓冲区内添加 item, 消费者通过 remove 函数在缓冲区内删除 item

信号量有 3 个, 分别为 empty, full, mutex. empty 表示缓冲区内的空格位置, insert 时需要检查缓冲区是否为空, remove 时需要检查缓冲区是否含有元素. mutex 提供对缓冲区的互斥访问

semaphore empty, full, mutex;

// init empty = size, full = 0, mutex = 1;

// java code
public void insert(Object item) {
empty.acquire();
mutex.acquire();

buffer[in] = item;
in = (in+1) % BUFFER_SIZE;

mutex.release();
full.release();
}

public Object remove() {
full.acquire();
mutex.acquire();

Object item = buffer[out];
out = (out + 1) % BUFFER_SIZE;

mutex.release();
empty.release();

return item;
}


  

注意, empty, full 都必须使用信号量来表示其剩余个数, 不能用 if 代替

2. 读者写者问题

// writer may starve

void read() {
while(true) {
mutext.acquire();
readCount ++;
if(readCount == 1) {
writeLock.acquire();
}
mutext.release();

do reading

mutext.acquire();
readCount --;
if(readCount == 0) {
writeLock.release();
}
mutext.release();
}
}

void write() {
writeLock.acquire();
do writing
writeLock.release();
}


  

6.7 管程

管程将 acquire, release 这些操作封装起来了. 管程确保一次只有一个进程能在管程内活动.

管程内的条件遍历有两个操作:

wait() 挂起调用进程并释放管程, 直至另一个进程在条件变量上执行 signal()

signal() 假如有因条件变量被挂起的线程, 那么释放之, 否则什么也不做.

管程实现生产者消费者问题

// full means that no space for new added item
// empty means that no item for consumer
monitor ProducerConsumer {
int itemCount;
condition full;
condition empty;

procedure add(item) {
if(itemCount == BUFFER_SIZE) {
wait(full);
}

putItemIntoBuffer(item);
itemCount ++;

if(itemCount == 1) {
signal(empty);
}
}

procedure remove() {
if(itemCount == 0) {
wait(empty);
}

removeItemFromBuffer();
itemCount --;

if(itemCount == BUFFER_SIZE-1)
signal(full);
}
}


管程示意图
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: