今天,我是数据库的BOS(读者-写者问题)

读者-写者问题

概述

数据库的管理通常涉及到数据的读取与写入,为数据库访问建立模型,可以允许同时读,但凡有一个在写其他的都不能读取,那么该如何实现呢?

注意:本文涉及到进程通信的信号量、临界区、互斥等知识,如有困惑请移步下方文章,目录中即可查找到相关概念的索引。

《现代操作系统》02章 进程与线程(一)

读者优先

我们用一个场景来模拟

数据库大厅有一个柜台,柜台内仅有一位柜员,这个柜员只上过小学一年级,只会加减法,有读者来了就加一、有读者走了就减一,如果柜台空闲他随机叫醒一位等待使用柜台的读者,他还知道现在是否有人占用数据库,并在数据库空闲时随机叫醒一位等待使用数据库的人。

想要读取数据的读者先要到柜台登记,如果柜台被人占用他就睡觉直到被叫醒。第一个读者负责把数据库打开,读取完数据后要再去柜台缴费结算并离开,最后一个读者负责关闭数据库,只要有读者来,数据库就不会关,直到所有的读者都读完了。

想要写数据的写者要先看看数据库有没有被读者或其他写者占用,如果占用了就小睡一会,直到被叫醒。

这就是现在这个数据库的管理规则,下面用C代码实现:

typedef int semaphore;
semaphore mutex = 1;//控制对rc的访问
semaphore db = 1;//控制对数据库的访问
int rc = 0;//正在读取的进程数

void reader(void)
{
	while(1)
	{
		down(&mutex); //看看readcounter柜台是否被人占用
		rc++; //在readcounter(柜台)处注册登记以便等待读取
		if(rc == 1)down(&db); //第一个读者打开数据库:数据库不能被修改
		up(&mutex); //柜台使用完毕
		read_data_base();
		down(&mutex); //看看readcounter柜台是否被人占用
		rc--; // 柜台没人占用,缴费离开(把名字划掉)
		if(rc == 0)up(&db); //最后一个读者关上数据库:表示没有人使用了
		up(&mutex); //柜台使用完毕
		use_data_read(); //用户处理读取到的数据
	}
}

void writer(void)
{
	while(1)
	{
		think_up_data(); //生产将要更新的数据
		down(&db); //数据库是否被人占用?
		write_data_base(); //没人占用修改数据
		up(&db); //数据库使用完毕
	}
}



停,请大家思考上面的代码有什么弊端!
停,请大家思考上面的代码有什么弊端!
停,请大家思考上面的代码有什么弊端!
停,请大家思考上面的代码有什么弊端!
停,请大家思考上面的代码有什么弊端!


是不是写者很卑微呀,有种读者优先的意味,这可能导致写者一觉不醒了!

写者优先

Courtois等人整出了一种写者优先的算法,但是《现代操作系统》书中并没有详细介绍,我去站内搜了一下,找到了这篇论文的翻译,读者和写者(Courtois 1971)论文翻译.在此感谢这位老哥的翻译。

论文中给出了伪代码和一些解释,我用更通俗易懂的方式来呈现,还是按照上面的逻辑来讲解,如有错误欢迎探讨

数据库大厅的门口有一个保安*(mutex3):保安不管写者,只管读者,当大厅内有一个读者在进行读取登记时,其他读者不可进入

数据库大厅内有两个柜台(mutex1、mutex2):readcounter柜台负责读者读取登记和缴费登记,writercounter柜台负责写者写入登记和缴费登记。柜台在访客登入和登出时查看公告板,叫醒相应的人

数据库大厅有一个公告板(write、read):公告板负责显示当前数据库的使用状态,如读者正在读取,写者正在写入,有读者等待,有写者等待

typedef int semaphore;
semaphore mutex1 = 1;//控制对rc的访问
semaphore mutex2 = 1;//控制对wc的访问
semaphore mutex3 = 1;//控制进入大厅的读者数量
semaphore read = 1;//读者对数据库的控制
semaphore write = 1;//写者对数据库的控制
int rc = 0,wc = 0;//读者和写者的计数器

void reader(void)
{
	while(1)
	{	
		//大厅入口咨询保安
		down(&mutex3); //查看是否可以进入大厅(大厅里只能有一个读者睡觉)
			//进入大厅查看公告板
			down(&read); //查看是否有写者在工作或睡觉,有写者工作或睡觉则睡觉,直到被叫醒
				//前往柜台
				down(&mutex1); //看看readcounter柜台是否被人占用
				rc++; //在readcounter(柜台)处注册登记以便等待读取
				if(rc == 1)down(&write); //第一个读者声明,数据库被读者占用
				up(&mutex1); //readcounter柜台使用完毕
			up(&read); //若有写者因read信号量在睡觉,叫醒他
		up(&mutex3); //若大厅外有读者等待,随机叫一个读者进入大厅
		read_data_base();
		down(&mutex); //看看readcounter柜台是否被人占用
		rc--; // 柜台没人占用,缴费离开(把名字划掉)
		if(rc == 0)up(&write); //最后一个读者声明,数据库可以修改了,如有写者因write在睡觉,叫醒他
		up(&mutex); //柜台使用完毕
		use_data_read(); //用户处理读取到的数据
	}
}

void writer(void)
{
	while(1)
	{
		think_up_data(); //生产将要更新的数据
		
		//前往柜台
		down(&mutex2); //看看writecounter柜台是否被人占用
		wc++; //在readcounter(柜台)处注册登记以便等待读取
		if(wc == 1)down(read); //若是第一个写者,查看是否有读者在登记,有则睡觉,直到被叫醒
		up(&mutex2); //writecounter柜台使用完毕,离开柜台
		
		//查看公告板
		down(&write); //数据库是否被人占用?未被占用则声称占用,被占用则睡觉
		write_data_base(); //没人占用修改数据
		up(&write); //数据库使用完毕,声称使用完毕
		
		down(&mutex2); //看看writecounter柜台是否被人占用
		wc--; // 柜台没人占用,缴费离开(把名字划掉)
		if(wc == 0)up(read); //最后一个写者工作完成,查看是否有读者因read睡觉,有则叫醒
		up(&mutex2); //writecounter柜台使用完毕,离开柜台
	}
}

这样可以保证,当最后一个读者读取完成时,他唤醒的一定是写者,并且当有写者到来并等待的时候,后来的读者无法完成登记。

因为写者一来就会执行一次down(&read);,这样后来的读者也只能被迫等待,而不可能再有其他读者进入。而当之前所有的读者读完后会执行一次up(&write),这时写者开始写了,只有所有的写者都写完才会执行up(read);,这时刚刚等待的读者才能有继续登记的权限。

我不禁叹服计算机学家的伟大,他们在前人的思想上不断地改进和创新,让程序运行的更加通畅和高效!


如果文中有误,还请在评论区指正。这里是海小皮,我们一同进步!!!

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值