概念理解
互斥:只能有一个进程访问共享资源或只能有一个线程访问临界区
同步:多个进程间的协作关系。进程A的执行需要等待进程B事件执行完成
异步:进程以不可预知的速度向前推进。在等待某事件的过程中,继续做自己的事,不需要等待事件完成后工作,通过线程实现
并发:进程间走走停停运行(在一个时间点上只运行一个进程,在一个时间段运行多个进程)
并行:多核处理器同时运行多个进程(在一个时间点上可以运行多个进程)
🔧经典同步互斥问题
🎋生产者-消费者问题
条件:
- 在任何时候只能有一个线程可以操作共享变量
- 允许同一时间有多个生产者或者多个消费者
- 当食物为0,需要等待生产者生产;当食物满了,需要等待消费者消费
解决方法:
- 信号量
- 管程
//使用信号量解决同步互斥问题
Class BoundedBuffer{
mutex = new Semaphore(1);
fullBuffers = new Semaphore(0);
emptyBuffers = new Semaphore(n);
}
BounderBuffer::Deposit(c){
emptyBuffers->P(); //解决同步问题
mutex->P(); //解决互斥问题
Add e to the buffer;
mutex->V();
fullBuffers->V();
}
BoundedBuffer::Remove(c){
fullBuffers->P();
mutex->P();
Remove e from buffer;
mutex->V();
emptyBuffers->V();
}
//使用管程实现同步互斥问题
Class BoundedBuffer{
Lock lock;
int count = 0;
Condition notFull,notEmpty;
}
BoundedBuffer::Deposit(e){
lock->Acquire(); //解决互斥问题
while(count == n){ //解决同步问题,唤醒后先release再执行其他线程
notFull.Wait(&lock);
}
Add c to the buffer;
count++;
notEmpty.Signal();
lock->Release();
}
BoundedBuffer::Remove(e){
lock->Acquire();
while(count == 0 ){
notEmpty.Wait(&lock);
}
Remove c from buffer;
count--;
notFull.Signal();
lock->Release();
}
🖊️读者-写者问题
条件:
- 在任何时候只能有一个线程可以操作共享变量
- 允许同一时间有多个读者,但在任何时候只有一个写者
- 写者在读者后面:写者需要等待读者读完;读者在写者后面:读者优先,因为读者不改变数据
解决方法:
- 信号量
- 管程
//使用信号量解决同步互斥问题(读者优先)
Class BoundedBuffer{
int Rcount = 0; //读者的数量
CountMutex = new Semaphore(1);
WriteMutex = new Semaphore(1);
}
BoundedBuffer::Write(){
sem_wait(WriteMutex);
write;
sem_post(WriteMutex);
}
BoundedBuffer::Read(){
sem_wait(CountMutex);
if(Rcount == 0){
sem_wait(WriteMutex);
}
Rcount++;
sem_post(CountMutex);
read;
sem_wait(CountMutex);
Rcount--;
if(Rcount == 0){
sem_post(WriteMutex);
}
sem_post(CountMutex);
}
//使用管程解决同步互斥问题(写者优先)
Class BoundedBuffer{
AR = 0; //active readers
AW = 0; //active writers
WR = 0; //waiting readers
WW = 0; //waiting writers
Condition okToRead;
Condition okToWrite;
Lock lock;
}
BuffedBuffer::Write(){
lock.Acquire();
while((AW+AR)>0){ //等待没有写者写和没有读者读,再进行写操作
WW++;
okToWrite.wait(&lock);
WW--;
}
AW++;
lock.Release();
write;
lock.Acquire();
AW--;
if(WW>0){ //写者优先,先唤醒等待队列中的写者
okToWrite.signal();
}else if(WR>0){ //没有等待的写者,再唤醒等待的读者
okToRead.broadcast();
}
lock.Release();
}
BuffedBuffer::Read(){
lock.Acquire(); //等待没有写者,再进行读操作
while((AW+WW)>0){
WR++;
okToRead.wait(&lock);
WR--;
}
AR++;
lock.Release();
read;
lock.Acquire(); //等待没有正在读的读者,再唤醒写线程
AR--;
if(AR == 0 && WW > 0){
okToWrite.signal();
}
lock.Release();
}
🍽️哲学家就餐问题
圆桌上有5个哲学家,每个哲学家之间都有一把叉子。哲学家有思考和进餐两个动作,进餐时需要同时拿起左右两边的叉子,思考时则同时放下叉子。如何保证哲学家动作有序进行?
Class BoundedBuffer{
int state[N]; //记录每个人的状态
mutex = new Semaphore(1); //互斥信号量
s[N] = new Semaphore(0); //同步信号量
}
void philosopher(int i){
while(TRUE){
think();
take_forks(i);
eat();
put_forks(i);
}
}
void take_forks(int i){
P(mutex);
state[i] = HUNGRY;
test_take_left_right_forks(i);
V(mutex);
P(s[i]); //没有叉子便阻塞
}
void put_forks(int i){
P(mutex);
state[i] = THINKING;
test_take_left_right_forks(LEFT);
test_take_left_right_forks(RIGHT);
V(mutex);
}
void test_take_left_right_forks(int i){
if(state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT] != EATING){
state[i] = EATING;
V(s[i]);
}
}