C语言实现读者写者问题(读者优先)

最近在做操作系统课程设计,碰到了这个问题,在解决的过程中也有找过不少别人的代码来做参考,但是在看他们代码的输出时,总感觉结果怪怪的。。后来还是自己琢磨了一个。我在代码中加入了一些语句,把中间结果输出来,这样就能明确看到各线程的状态。这结果应该是没有问题的了。如果有路过的大牛发现问题,欢迎在评论区留言向我反馈~非常感谢!

读者优先的伪代码:

void Reader()
{
    while(true)
    {
        P(x);
         rc++;
         if(rc == 1) then P(w);
        V(x);
        ...read...
        P(x);
         rc--;
         if(rc == 0) then V(w);
        V(x);
    }
}
void Writer()
{
    while(true)
    {
        P(w);
         ...write...
        V(w);
    }
}

这里的x和w(具体说明见代码注释),我用信号量来实现,x和w的初值均为1,实现代码如下:

#include <windows.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <accctrl.h>
unsigned int rc = 0;
bool p_ccontinue = true; //控制程序结束
HANDLE XSemaphore; //改变rc值时互斥
HANDLE WSemaphore; //写互斥
DWORD WINAPI Writer(LPVOID); //写者线程
DWORD WINAPI Reader(LPVOID); //读者线程
int main()
{
    XSemaphore = CreateSemaphore(NULL,1,1,NULL);
    WSemaphore = CreateSemaphore(NULL,1,1,NULL);
    const unsigned short Writer_COUNT = 3;//写者数量
    const unsigned short Reader_COUNT = 8;//读者数量
    const unsigned short THREADS_COUNT = Writer_COUNT+Reader_COUNT;
    HANDLE hThreads[THREADS_COUNT]; //各线程的 handle
    DWORD writerID[Writer_COUNT]; //写者线程的标识符
    DWORD readerID[Reader_COUNT]; //读者线程的标识符
    for (int i=0; i<Writer_COUNT; ++i)
    {
        hThreads[i]=CreateThread(NULL,0,Writer,NULL,0,&writerID[i]);
        if (hThreads[i]==NULL) return -1;
    }
    for (int i=0; i<Reader_COUNT; ++i)
    {
        hThreads[Writer_COUNT+i]=CreateThread(NULL,0,Reader,NULL,0,&readerID[i]);
        if (hThreads[Writer_COUNT+i]==NULL) return -1;
    }
    while(p_ccontinue)
    {
        if(getchar())  //按回车后终止程序运行
        {
            p_ccontinue = false;
        }
    }
    return 0;
}
//写者
DWORD WINAPI Writer(LPVOID lpPara)
{
    while(p_ccontinue)
    {
        printf("%d is waiting to write...\n",GetCurrentThreadId());
        WaitForSingleObject(WSemaphore,INFINITE);//P(w)
        printf("**  writer %d got the control\n",GetCurrentThreadId());
        //write...
        printf("%d is writing...\n",GetCurrentThreadId());
        Sleep(1500);
        printf("--- %d have finished the writing\n",GetCurrentThreadId());
        ReleaseSemaphore(WSemaphore,1,NULL);//V(w)
        return 0;//结束此线程
    }
    return 0;
}
//读者
DWORD WINAPI Reader(LPVOID lpPara)
{
    while(p_ccontinue)
    {
        printf("%d is waiting to read...\n",GetCurrentThreadId());
        WaitForSingleObject(XSemaphore,INFINITE);//P(x)
        rc++;
        if(rc == 1)//第一个读,要等已经在写的写者写完才可以开始读
            WaitForSingleObject(WSemaphore,INFINITE);//P(w)
        //同时读,故要在Read()之前V(x)
        ReleaseSemaphore(XSemaphore,1,NULL);//V(x)
        //read...
        printf("%d is reading...\n",GetCurrentThreadId());
        Sleep(1500);
        printf("--- %d have finished the reading\n",GetCurrentThreadId());
        WaitForSingleObject(XSemaphore,INFINITE);//P(x)
        rc--;
        if(rc == 0)
        {
            printf("----- All Readers done\n");
            ReleaseSemaphore(WSemaphore,1,NULL);//V(w)
        }
        ReleaseSemaphore(XSemaphore,1,NULL);//V(x)
        return 0;//结束此线程
    }
    return 0;
}

运行结果如下:

228 is waiting to write...
16112 is waiting to write...
15996 is waiting to read...
7744 is waiting to write...
16076 is waiting to read...
15016 is waiting to read...
15596 is waiting to read...
14912 is waiting to read...
**  writer 228 got the control
228 is writing...
14740 is waiting to read...
2704 is waiting to read...
6344 is waiting to read...
--- 228 have finished the writing
**  writer 16112 got the control
16112 is writing...
--- 16112 have finished the writing
15996 is reading...
2704 is reading...
15016 is reading...
6344 is reading...
16076 is reading...
14912 is reading...
15596 is reading...
14740 is reading...
--- 2704 have finished the reading
--- 15996 have finished the reading
--- 15016 have finished the reading
--- 16076 have finished the reading
--- 6344 have finished the reading
--- 15596 have finished the reading
--- 14740 have finished the reading
--- 14912 have finished the reading
----- All Readers done
**  writer 7744 got the control
7744 is writing...
--- 7744 have finished the writing

可以得出:
当有读者在读时,若此时又来了读者和写者,那么这个新来的读者会优先读,而不会让写者去写;
当所有的读者读完之后才会放权让写者去写;
而当有写者在写的时候,也不会有其他读者和写者进行读写操作。

另外,还有一个需要注意的点:
在每一个读者或写者线程完成了自己的操作后,需要结束这些线程。因为如果不结束的话,那么就会出现读者读完还等着读,写者永久得不到操作的机会。所以我在Reader和Writer里面(while循环里)都加了return这个语句,以结束线程。


2020/4/27更

评论区反映了等待队列的问题,可能之前那个读者和写者出现的随机性不够,且出现的时间模糊不清,所以才会摸不清读者和写者的等待队列。

我后面改了一下,把读者和写者的出现设为随机出现(产生0-9的随机数,0-4为写者,5-9为读者),然后在每一个读者和写者出现之后使系统sleep(500),其他保持不变,这样的话就可以比较清楚的看出是谁先来,谁在等,代码如下:

#include <windows.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <accctrl.h>
#include <time.h>
unsigned int rc = 0;
bool p_ccontinue = true; //控制程序结束
HANDLE XSemaphore; //改变rc值时互斥
HANDLE WSemaphore; //写互斥
DWORD WINAPI Writer(LPVOID); //写者线程
DWORD WINAPI Reader(LPVOID); //读者线程
int main()
{
    XSemaphore = CreateSemaphore(NULL,1,1,NULL);
    WSemaphore = CreateSemaphore(NULL,1,1,NULL);
    const unsigned short Writer_COUNT = 3;//写者数量
    const unsigned short Reader_COUNT = 8;//读者数量
    const unsigned short THREADS_COUNT = Writer_COUNT+Reader_COUNT;
    HANDLE hThreads[THREADS_COUNT]; //各线程的 handle
    DWORD writerID[Writer_COUNT]; //写者线程的标识符
    DWORD readerID[Reader_COUNT]; //读者线程的标识符

    srand(time(0));
    int writer_count = 0;
    int reader_count = 0;
    for(int i = 0; i < Writer_COUNT + Reader_COUNT; i++)
    {
        int ran = rand()%9;
        if((ran < 5) && (writer_count < Writer_COUNT))//Writer
        {
            hThreads[i]=CreateThread(NULL,0,Writer,NULL,0,&writerID[writer_count]);
            if (hThreads[i]==NULL) return -1;
            writer_count++;
        }
        else if((ran > 5) && (reader_count < Reader_COUNT))//Reader
        {
            hThreads[i]=CreateThread(NULL,0,Reader,NULL,0,&readerID[reader_count]);
            if (hThreads[i]==NULL) return -1;
            reader_count++;
        }
        Sleep(500);//等待500ms
    }
    while(p_ccontinue)
    {
        if(getchar())  //按回车后终止程序运行
        {
            p_ccontinue = false;
        }
    }
    return 0;
}
//写者
DWORD WINAPI Writer(LPVOID lpPara)
{
    while(p_ccontinue)
    {
        printf("\n%d is waiting to write...\n",GetCurrentThreadId());
        WaitForSingleObject(WSemaphore,INFINITE);//P(w)
        printf("\n**  writer %d got the control\n",GetCurrentThreadId());
        //write...
        printf("\n%d is writing...\n",GetCurrentThreadId());
        Sleep(1500);
        printf("\n--- %d have finished the writing\n",GetCurrentThreadId());
        ReleaseSemaphore(WSemaphore,1,NULL);//V(w)
        return 0;//结束此线程
    }
    return 0;
}
//读者
DWORD WINAPI Reader(LPVOID lpPara)
{
    while(p_ccontinue)
    {
        printf("\n%d is waiting to read...\n",GetCurrentThreadId());
        WaitForSingleObject(XSemaphore,INFINITE);//P(x)
        rc++;
        if(rc == 1)//第一个读,要等已经在写的写者写完才可以开始读
            WaitForSingleObject(WSemaphore,INFINITE);//P(w)
        //同时读,故要在Read()之前V(x)
        ReleaseSemaphore(XSemaphore,1,NULL);//V(x)
        //read...
        printf("\n%d is reading...\n",GetCurrentThreadId());
        Sleep(1500);
        printf("\n--- %d have finished the reading\n",GetCurrentThreadId());
        WaitForSingleObject(XSemaphore,INFINITE);//P(x)
        rc--;
        if(rc == 0)
        {
            printf("\n----- All Readers done\n");
            ReleaseSemaphore(WSemaphore,1,NULL);//V(w)
        }
        ReleaseSemaphore(XSemaphore,1,NULL);//V(x)
        return 0;//结束此线程
    }
    return 0;
}//MIC_H_2020-4-27

运行结果如下:


2612 is waiting to write...

**  writer 2612 got the control

2612 is writing...

10560 is waiting to read...

7152 is waiting to write...

--- 2612 have finished the writing

10560 is reading...

9868 is waiting to write...

14112 is waiting to read...

14112 is reading...

--- 10560 have finished the reading

8384 is waiting to read...

8384 is reading...

--- 14112 have finished the reading

--- 8384 have finished the reading

----- All Readers done

**  writer 7152 got the control

7152 is writing...

--- 7152 have finished the writing

**  writer 9868 got the control

9868 is writing...

--- 9868 have finished the writing

另外,根据程序运行逻辑,附上我画的一个时间轴:

从中就可以看出当读者在读的时候,如果出现新读者和新写者,新读者会同时读,如图中的R10560、W7152、W9866、R14112,当R10560读完的时候,R14112出现了,就算R14112比W7152和W9866晚出现,依然是执行读操作,由此可看出本程序实现的是读者优先。

当然,不排除系统的延迟其他因素的影响,这个时间轴和真实结果不会百分百重合,但大体上是一致的。

此博客为原创内容,仅供学习交流,转载请注明出处:https://blog.csdn.net/qq_41759198/article/details/92805813

 

  • 26
    点赞
  • 112
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值