基于信号量的进程同步与互斥(2)

基于信号量的进程同步与互斥(2)

1. 三个进程P1、P2、P3 互斥使用一个包含N(N>0)个单元的缓冲区。P1 每次用produce()生成一个正整数并用put()送入缓冲区某一个空单元中;P2 每次用getodd()从该缓冲区中取出一个奇数并用countodd()统计奇数个数;P3 每次用 geteven()从该缓冲区中取出一个偶数并用counteven()统计偶数个数。请用信号量机制实现这三个进程的同步与互斥活动,并说明所定义的信号量的含义。

本题主要考察生产者-消费者的变形,一个生产者对应两个消费者,注意要写cobegin-coend等语句

(1)缓冲区是一互斥资源,因此设互斥信号量mutex;

(2)同步问题:P1、P2因为奇数的放置与取用而同步,设同步信号量odd;P1、P3因为偶数的放置与取用而同步,设同步信号量even;P1、P2、P3因为共享缓冲区,设同步信号量empty。

semaphore mutex = 1, odd = 0, even = 0, empty = N;
main()
cobegin{
    Process P1
    while (true) {
        number = produce();
        P(empty);
        P(mutex);
        put();
        V(mutex);
        if number % 2 == 0
            V(even);
        else
            V(odd);
    }

    Process P2
    while (true) {
        P(odd);
        P(mutex);
        getodd();
        V(mutex);
        V(empty);
        countodd();
    }

    Process P3
    while (true) {
        P(even);
        P(mutex);
        geteven();
        V(mutex);
        V(empty);
        counteven();
    }
}coend

2. 一个野人部落从一个大锅中一起吃炖肉,这个大锅一次可以存放 M 人份的炖肉。当野人们想吃的时候,如果锅中不空,他们就自助着从大锅中吃肉。如果大锅空了,他们就叫醒厨师,等待厨师再做一锅肉。

野人线程未同步的代码如下:

while (true){ 
	getServingFromPot();
	eat();
}

厨师线程未同步的代码如下:

while (true) {
	putServingsInPot(M);
}

同步的要求是:当大锅空的时候,野人不能够调用getServingFromPot();仅当大锅为空的时候,大厨才能够调用putServingsInPot()

问题:请写出满足同步要求的野人线程和厨师线程的同步原语。

servings = 0 //表示锅中肉的份数
mutex = Semaphore(1) //互斥量保护serving
emptyPot = Semaphore(0) //pot为空
fullPot = Semaphore(0) //pot为满

厨师的同步代码:
while (true) {
    P(emptyPot)
    putServingsInPot(M)
    V(fullPot)
}

野人的同步代码
while (true) {
    P(mutex)
    if (servings == 0) {
        V(emptyPot)
        P(fullPot)
        servings = M
    }
    servings -= 1
    getServingFromPot()
    V(mutex)
    eat()
}

3. 系统中有多个生产者进程和消费者进程,共享用一个可以存 1000 个产品的缓冲区(初始为空),当缓冲区为未满时,生产者进程可以放入一件其生产的产品,否则等待;当缓冲区为未空时,消费者进程可以取走一件产品,否则等待。要求一个消费者进程从缓冲区连续取出 10 件产品后,其他消费者进程才可以取产品,请用信号量 P,V 操作实现进程间的互斥和同步,要求写出完整的过程;并指出所用信号量的含义和初值。

​ 本题是一个生产者-消费者的变型,本题是多个生产者-多个消费者类型,生产者和消费者之间、生产者和生产者之间、消费者和消费者之间要互斥访问缓冲区,并且本题的消费者一次需取走10件产品,如果没有它会等待,而不是等到有了10件产品后,才进行取走操作。

​ 本题的缓冲区B可描述为 buffer array[1000];

​ (1)生产者之间,生产者消费者之间设互斥信号量mutex1,消费者之间设互斥信号量mutex2.

​ (2)上述进程的同步问题,需设置3个信号量,其中empty对应空闲的缓冲单元,初值为1000;full对应缓冲区中待取走的产品数,初值为0;另外,还需定义2个整形变量in、out,分别用来指示下一个可存放产品的缓冲单元、下一个可取走的缓冲单元,它们的初值均为0。

buffer array[1000]; //存放产品的缓冲区
buffer nextp; //用于临时存放生产者生产的产品
buffer nextc[10]; //用于临时存放消费者取出的产品
semaphore empty = 1000; //空缓冲区的数目
semaphore full = 0; //满缓冲区的数目
semaphore mutex1 = 1; //用于生产者之间的互斥,以及生产者消费者互斥
semaphore mutex2 = 1; //用于消费者之间的互斥
int in = 0; //指示生产者的存位置
int out = 0; //指示消费者的取位置

Producer() { //生产者进程
    Produce an item put in nextp; //生产一个产品,存在临时缓冲区
    P(empty); //申请一个空缓冲区
    P(mutex1); //生产者申请使用缓冲区
    array[in] = nextp; //将产品存入缓冲区
    in = (in + 1) % 1000; //指针后移
    V(mutex1); //生产者缓冲区使用完毕,释放互斥信号量
    V(full); //增加一个满缓冲区
}

Consumer() { //消费者进程
    P(mutex2); //消费者申请使用缓冲区
    for (int i = 0; i < 10; i++) { //一个消费者进程需从缓冲区连续取走10件产品
        P(full); //申请一个满缓冲区
        P(mutex1); //互斥生产者
        nextc[i] = array[out]; //将产品取出,存于临时缓冲区
        out = (out + 1) % 1000; //指针后移
        V(mutex1); //解除生产者互斥
        V(empty); //增加一个空缓冲区
    }
    V(mutex2); //消费者缓冲区使用完毕,释放互斥信号量
    Consume the items in nextc; //消费掉这10个产品
}

4. 读者——写者问题(写者优先)

信号量定义
readSwitch = Lightswitch()
writeSwitch = Lightswitch()
noReaders = Semaphore(1)
noWriters = Semaphore(1)

class Lightswitch:
	def __init__(self):
		self.counter = 0 //封装计数器和互斥锁
		self.mutex = Semaphore(1)
	def lock(self, semaphore): //信号量是参数
		self.mutex.wait()
		self.counter += 1
		if self.counter == 1:
			semaphore.wait()
		self.mutex.signal()
	def unlock(self, semaphore):
		self.mutex.wait()
		self.counter -= 1
		if self.counter == 0:
			semaphore.signal()
		self.mutex.signal()

​ 最初信号量都是解锁态。如果reader在临界区,会给noWriter上锁,但是不会给noReader上锁。如果writer到来,会给noReader加锁,会让后续读者排队在noReader。当最后一个读者离开,他会signal noWriter,这时写者可以进入。

​ 当写者进入临界区,他同时拿着noReader和noWriter两个锁。一方面,其他读者和写者不能同时访问临界区。另一方面,writeSwitch允许其他写者通过,并在noWriter等待。但是读者只能在noReader等待。这样,所有排队的写者能够通过临界区,而不需要signal noReader。当最后一个写者离开,noReader才解锁。

Reader:
noReaders.wait()
	readSwitch.lock(noWriters)
noReaders.signal()
	# critical section for readers
readSwitch.unlock(noWriters)

Writer:
writeSwitch.lock(noReaders)
	noWriters.wait()
		# critical section for writers
	noWriters.signal()
writeSwitch.unlock(noReaders)
semaphore noReaders = 1
semaphore noWriters = 1
int readers = 0
semaphore readers_mutex = 1
int writers = 0
semaphore writers_mutex = 1
semaphore 
main()
cobegin{
	Process Reader
	P(noReaders);
	P(readers_mutex);
	readers += 1;
	if (readers == 1) {
		P(noWriters);
	}
	V(readers_mutex);
	V(noReaders);
	#critical section for readers
	P(readers_mutex);
	readers -= 1;
	if (readers == 0) {
		V(noReaders);
	}
	V(readers_mutex);
	
	Process Writer
	P(writers_mutex);
	writers += 1;
	if (writers == 1) {
		P(noReaders);
	}
	V(writers_mutex);
	P(noWriters);
	# critical section for writers
	V(noWriters);
	P(writers_mutex);
	writers -= 1;
	if (writers == 0) {
		V(noReaders);
	}
	V(writers_mutex);
}coend

5. 寿司店问题。假设一个寿司店有 5 个座位,如果你到达的时候有一个空座位,你可以立刻就坐。但是如果你到达的时候 5 个座位都是满的有人已经就坐,这就意味着这些人都是一起来吃饭的,那么你需要等待所有的人一起离开才能就坐。编写同步原语,实现这个场景的约束。

eating = waiting = 0
mutex = Semaphore(1)
block = Semaphore(0)
must_wait = False

P(mutex)
if must_wait: //需要等待所有人离开
	waiting += 1
	V(mutex)
	P(block) //在block上睡眠
else: //可以直接吃,如果就坐后满了,后续需要等待
	eating += 1
	must_wait = (eating == 5)
	V(mutex)
# eat sushi
P(mutex)
eating -= 1 //吃完了
if eating == 0: //如果最后一个顾客,可唤醒block上等待的顾客
	n = min(5, waiting) //最多5个顾客可以吃
	waiting -= n
	eating += n
	must_wait = (eating == 5) //reset must_wait
	for (int i = 0; i < n; i++) {
		V(block)
	}
V(mutex)

6. 搜索-插入-删除问题。三个线程对一个单链表进行并发的访问,分别进行搜索、插入和删除。搜索线程仅仅读取链表,因此多个搜索线程可以并发。插入线程把数据项插入到链表最后的位置;多个插入线程必须互斥防止同时执行插入操作。但是,一个插入线程可以和多个搜索线程并发执行。最后,删除线程可以从链表中任何一个位置删除数据。一次只能有一个删除线程执行;删除线程之间,删除线程和搜索线程,删除线程和插入线程都不能同时执行。

semaphore noSearch = 1; //表示临界区无搜索进程
semaphore noInsert = 1; //表示临界区无插入进程
semaphore iMutex = 1; //用于插入进程之间的互斥
int insertNum = 0; //表示插入进程的数目
semaphore mutexInsertNum = 1; //保护对insertNum的互斥访问
int searchNum = 0; //表示搜索进程的数目
semaphore mutexSearchNum = 1; //保护对searchNum的互斥访问

Process Delete
P(noSearch);
P(noInsert);
delete();
V(noInsert);
V(noSearch);

Process Insert
P(mutexInsertNum);
insertNum++;
if (insertNum == 1) {
	P(noInsert);
}
V(mutexInsertNum);
P(iMutex);
insert();
V(iMutex);
P(mutexInsertNum);
insertNum--;
if (insertNum == 0) {
	V(noInsert);
}
V(mutexInsertNum);

Process Search
P(mutexSearchNum);
searchNum++;
if (searchNum == 1) {
	P(noSearch);
}
V(mutexSearchNum);
search();
P(mutexSearchNum);
searchNum--;
if (searchNum == 0) {
	V(noSearch);
}
V(mutexSearchNum);

以上部分内容引自课件,如有侵权,请及时联系我删除!

posted @ 2020-06-07 00:06  韩程凯  阅读(73)  评论(0编辑  收藏