经典进程同步问题——读者写者问题

问题描述

写者优先

要求:
为了防止“读者优先”可能导致的写者饥饿,可以考虑写者优先。即,当共享数据区被读者占用时,后续紧邻到达的读者可以继续进入,若这时有一个写者到来并阻塞等待,则写者后续到来的读者全部阻塞等待。即只要有一个写者申请写数据,则不再允许新的读者进程进入读数据。这样,写者只需等待先于它到达的读者完成其读数据的任务,而不用等待其后到达的读者。

新读者:
如果无读者、写者,新读者可以读。
如果有写者等待,且有其它读者正在读,新读者等待。
如果有写者写,新读者等待。

新写者:
如果无读者,新写者可以写。
如果有读者,新写者等待。
如果有其它写者,新写者等待。

思路:
同读者优先有一些类似,写者优先也需要用一个整型变量Readcount记录当前的读者数目,用于确定是否需要释放正在等待的写者线程(当Readcount=0时,表明所有的读者都已经读完,需要释放写者等待队列中的一个写者)。在每一个读者读文件之前,都必须修Readcount变量的值(增一),在每一个读者读完文件之后,也必须修改Readcount变量的值(减一)。因此需要一个互斥对象Rmutex来实现对
全局变量Readcount修改时的互斥。
为了实现写-写互斥,需要增加一个写互斥信号量Wmutex。保证写进程与其它进程写互斥地访问数据集。
另外,还需要增加一个信号量 S,初值为 1,用来实现读写互斥,使写者请求发生后的读者等待。

综上,设置一个共享变量和三个互斥信号量。
共享变量 Readcount:记录当前正在读数据集的读进程数目,初值为 0。
读互斥信号量 Rmutex :表示读进程互斥地访问共享变量 Readcount,初值为 1。
写互斥信号量 Wmutex:表示写进程与其它进程(读、写)互斥地访问数据集,初值为 1。
读写互斥信号量 S :表示读进程和写进程互斥地访问数据集,初值为 1。

//写者优先

# include 
# include 
# include 
# include 
# include 
# include 
# include 
# include 

sem_t Rmutex,Wmutex, S;
int readCount;

struct data {
	int id;
	int opTime;
	int lastTime;
};

//读者
void* Reader(void* param) {
	int id = ((struct data*)param)->id;
	int lastTime = ((struct data*)param)->lastTime;
	int opTime = ((struct data*)param)->opTime;

	sleep(opTime);
	printf("Thread %d: waiting to read\n", id);	

	sem_wait(&S);
	sem_wait(&Rmutex);
	readCount++;
	if(readCount == 1)
		sem_wait(&Wmutex);
	sem_post(&Rmutex);
	sem_post(&S);

//执行读操作
	printf("Thread %d: start reading\n", id);
	sleep(lastTime);
	printf("Thread %d: end reading\n", id);

	sem_wait(&Rmutex);
	readCount--;
	if(readCount == 0)
		sem_post(&Wmutex);
	sem_post(&Rmutex);

	pthread_exit(0);
}

//写者
void* Writer(void* param) {
	int id = ((struct data*)param)->id;
	int lastTime = ((struct data*)param)->lastTime;
	int opTime = ((struct data*)param)->opTime;

	sleep(opTime);
	printf("Thread %d: waiting to write\n", id);
	
	sem_wait(&S);
	sem_wait(&Wmutex);

//执行写操作
	printf("Thread %d: start writing\n", id);
	sleep(lastTime);
	printf("Thread %d: end writing\n", id);

	sem_post(&Wmutex);
	sem_post(&S);
	
	pthread_exit(0);
}

int main() {
	pthread_t tid; 
	
	//初始化信号量
    	 sem_init(&Rmutex, 0, 1);
    	 sem_init(&Wmutex, 0, 1);
	 sem_init(&S, 0, 1);

   	readCount = 0;
	
	int id = 0;
	while(scanf("%d", &id) != EOF) {

		char role;		//读者还是写者
		int opTime;		//每个进程执行间隔时间
		int lastTime;	//读/写的时间

		scanf("%c%d%d", &role, &opTime, &lastTime);
		struct data* d = (struct data*)malloc(sizeof(struct data));

		d->id = id;
		d->opTime = opTime;
		d->lastTime = lastTime;

		if(role == 'R') {
			printf("Create the %d thread: Reader\n", id);
			pthread_create(&tid, NULL, Reader, d);

		}
		else if(role == 'W') {
			printf("Create the %d thread: Writer\n", id);
			pthread_create(&tid, NULL, Writer, d);
		}
	}

	sem_destroy(&S);
	sem_destroy(&Rmutex);
	sem_destroy(&Wmutex);

	return 0;
}

读者优先

要求:
当一个读者正在读数据时,另一个读者也需要读数据,应允许第二个读者进入,同理第三个及后续读者都应允许进入。现在假设一个写者到来,由于写操作是排他的,所以它不能访问数据,需要阻塞等待。如果一直有新的读者陆续到来,写者的写操作将被严重推迟。该方法称为“读者优先”,即一旦有读者正在读数据,只有当全部读者退出,才允许写者进入写数据。

新读者:
如果无读者、写者,新读者可以读。
如果有写者等待,但有其它读者正在读,则新读者也可以读。
如果有写者写,新读者等待。
新写者:
如果无读者,新写者可以写。
如果有读者,新写者等待。
如果有其它写者,新写者等待。

思路:
读者优先指的是除非有写者在写文件,否则读者不需要等待。所以可以用一个整型变量Readcount记录当前的读者数目,用于确定是否需要释放正在等待的写者线程(当Readcount=0时,表明所有的读者都已经读完,需要释放写者等待队列中的一个写者)。在每一个读者读文件之前,都必须修改Readcount变量的值(增一),在每一个读者读完文件之后,也必须修改Readcount变量的值(减一)。因此需要一个互斥对象Rmutex来实现对全局变量Readcount修改时的互斥。
另外,为了实现写-写互斥,需要增加一个写互斥信号量Wmutex。保证写进程与其它写进程互斥地访问数据集。
通过这种方法,也可以实现读-写互斥,当Readcount=1时(即第一个读者到来时),读者线程也必须P(Wmutex),此时写者阻塞。当写者P(Wmutex)后,接下来再到来的第一个读者判断完Readcount=1后阻塞在P(Wmutex)这一步上无法释放Rmutex,其余的读者由于等待第一个读者对Rmutex的释放而阻塞在P(Rmutex)上。

综上,设置一个共享变量和两个互斥信号量。
共享变量 Readcount:记录当前正在读数据集的读进程数目,初值为 0。
读互斥信号量 Rmutex :表示读进程互斥地访问共享变量 Readcount,初值为 1。
写互斥信号量 Wmutex:表示写进程与其它进程(读、写)互斥地访问数据集,初值为 1。

// 读者优先

# include 
# include 
# include 
# include 
# include 
# include 
# include 
# include 

sem_t Rmutex,Wmutex;
int readCount;

struct data {
    int id;
    int opTime;
    int lastTime;
};

//读者
void* Reader(void* param) {
    int id = ((struct data*)param)->id;
    int lastTime = ((struct data*)param)->lastTime;
    int opTime = ((struct data*)param)->opTime;

    sleep(opTime);
    printf("Thread %d: waiting to read\n", id);
    sem_wait(&Rmutex);
    readCount++;
    if(readCount == 1) 
        sem_wait(&Wmutex);
    sem_post(&Rmutex);

//执行读操作
    printf("Thread %d: start reading\n", id);
    sleep(lastTime);
    printf("Thread %d: end reading\n", id);

    sem_wait(&Rmutex);
    readCount--;
    if(readCount == 0)
        sem_post(&Wmutex);
    sem_post(&Rmutex);
    pthread_exit(0);
}

//写者
void* Writer(void* param) {
    int id = ((struct data*)param)->id;
    int lastTime = ((struct data*)param)->lastTime;
    int opTime = ((struct data*)param)->opTime;

    sleep(opTime);
    printf("Thread %d: waiting to write\n", id);
    sem_wait(&Wmutex);

//执行写操作
    printf("Thread %d: start writing\n", id);
    sleep(lastTime);
    printf("Thread %d: end writing\n", id);

    sem_post(&Wmutex);
    pthread_exit(0);
}

int main() {
    pthread_t tid; 

    //初始化信号量
    sem_init(&Rmutex, 0, 1);
    sem_init(&Wmutex, 0, 1);
    readCount = 0;

    int id = 0;
    while(scanf("%d", &id) != EOF) {
        char role;         //读者还是写者
        int opTime;     //每一个进程开始前的间隔时间
        int lastTime;   //执行读/写操作的时间

        scanf("%c%d%d", &role, &opTime, &lastTime);
        struct data* d = (struct data*)malloc(sizeof(struct data));

        d->id = id;
        d->opTime = opTime;
        d->lastTime = lastTime;

        if(role == 'R') {
            printf("Create the %d thread: Reader\n", id);
            pthread_create(&tid, NULL, Reader, d);

        }
        else if(role == 'W') {
            printf("Create the %d thread: Writer\n", id);
            pthread_create(&tid, NULL, Writer, d);
        }
    }

    //信号量销毁
    sem_destroy(&Rmutex);
    sem_destroy(&Wmutex);

    return 0;
}

你可能感兴趣的