读者写者问题
读者-写者问题
读者写者问题是并发和同步领域的经典问题,然而各大教科书和网络资源基本都只讲解了其中的一种——读者优先的情况,对其余情况涉及很少。本着深入研究问题的态度,在此对各种情况讨论并给出代码,仅供参考。以下说明中为简化把信号量都看做锁。
读者优先
这是os教科书里基本都会给出的情况,即
- 写者必须等待所有读者读完才可以写
- 多个读者可以同时读取,只要此时写者未获得权限
这个问题包含了读写互斥、写写互斥、允许多个读线程同时读取的意思。
此问题需要两个信号量,兼有加锁互斥和同步的功能。
读写线程通过writer_mutex
实现互斥,但读线程的reader_mutex
仅作为锁用来保证对readcount
的修改正确。只要有一个读线程进入reading区,写者就被阻塞,而其余读者仍可以通过持有并释放reader_mutex
来进入reading区,直到读者数为0,此时唤醒写者;而只要写者进入writing区,任意读者都会被阻塞(其中一个在P(&writer_mutex)
,其余在P(&reader_mutex)
)。
sem_t writer_mutex;
sem_t reader_mutex;
void* reader(){
while(1){
P(&reader_mutex);
readcount++;
if(readcount==1)
{
P(&writer_mutex);
}
V(&reader_mutex);
// reading is perfomanced
printf("how many readers:%d\n",readcount);
P(&reader_mutex);
readcount--;
if(readcount==0)
{
V(&writer_mutex);
}
V(&reader_mutex);
}
}
void* writer(){
while(1){
P(&writer_mutex);
writecount++;
// writing is perfomanced
printf("how many writers:%d\n",writecount);
writecount--;
V(&writer_mutex);
}
}
总体上来看这部分代码实现了所需功能,但也带来了缺陷:写者只能有一个且需要等待所有读者读完才能写,而只要有一个读者进入,读者就可以不受限制地进入,结果导致写进程饥饿。
写者优先
写者优先要求解决上面的饥饿问题,要求只要有写者在写,后续写者都可以写,读者必须等所有写者写完才能读。
需要四个信号量,mutex
用于为写者计数器加锁,reader_mutex
用于为读者计数器加锁,writer_mutex
实现写者互斥,queue
实现读线程排队。
每个读线程都要获取queue
,之后在真正做读操作前即让出,这时写线程就可以获得queue
,其实本质上就是给了写线程打断读者、并且持续占有锁的机会(读者优先里写线程没这种机会)。写线程获得queue
之后就一直占着,直到所有写线程都完成,提高了写线程优先级。
sem_t mutex,queue;
sem_t writer_mutex;
sem_t reader_mutex;
void reader(){
while(1){
P(&queue);
P(&reader_mutex);
readcount++;
if(readcount==1)
{
P(&writer_mutex);
}
V(&reader_mutex);
V(&queue);
printf("how many readers:%d\n",readcount);
P(&reader_mutex);
readcount--;
if(readcount==0)
{
V(&writer_mutex);
}
V(&reader_mutex);
}
}
void writer(){
while(1){
P(&mutex);
writecount++;
if(writecount==1){
P(&queue);
}
V(&mutex);
P(&writer_mutex);
printf("how many readers:%d\n",readcount);
V(&writer_mutex);
P(&mutex);
writecount--;
if(writecount==0){
V(&queue);
}
V(&mutex);
}
}
公平竞争
读者写者都在queue
上排队,即使A类线程有多个同时进入,B类线程也可以获得queue
而使A类其他线程阻塞,不会像前两种那样一但A类线程获得优势后B类线程很难再进入(读优先里读者获得writer_mutex
后,后续读者可以不断进入导致写者很难获得writer_mutex
;写优先里写者获得queue
后,写者可以不断进入导致读者很难获得queue
)
sem_t queue;
sem_t writer_mutex;
sem_t reader_mutex;
void* reader(){
while(1){
P(&queue);
P(&reader_mutex);
readcount++;
if(readcount==1)
{
P(&writer_mutex);
}
V(&reader_mutex);
V(&queue);
printf("how many readers:%d, how many writers:%d\n",readcount,writecount);
P(&reader_mutex);
readcount--;
if(readcount==0)
{
V(&writer_mutex);
}
V(&reader_mutex);
}
}
void* writer(){
while(1){
P(&queue);
P(&writer_mutex);
writecount++;
printf("how many readers:%d, how many writers:%d\n",readcount,writecount);
writecount--;
V(&writer_mutex);
V(&queue);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律