博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
读者写者问题(一)
阅读量:3782 次
发布时间:2019-05-22

本文共 6128 字,大约阅读时间需要 20 分钟。

阅读MoreWindows大神的至第十一篇,做一点小小的总结;

读者写者问题描述:有一个写者很多读者,多个读者可以同时读文件,但写者在写文件时不允许有读者在读文件,同样有读者在读文件时写者也不去能写文件,很简单的一个描述。

本文对于写者开始写文件时就将可读事件(g_hEventCanRead)设置为未触发状态,结束写作时将可读事件(g_hEventCanRead)设置为触发状态;对于多个读者,使用一计数器ReadCount计数正在阅读的读者个数,当ReadCount=1时,设置可写事件(g_hEventCanWrite)为未触发状态,当ReadCount=0时,设置可写事件(g_hEventCanWrite)为触发状态。以此来保证读者与写者的同步与互斥。

程序1:

#include 
#include
#include
const int READ_NUM = 5;CRITICAL_SECTION g_cs,g_cs_reader_count;/*g_cs用来确保对stdout输出端的操作完整性;g_cs_reader_count确保对readerCount计数的原子性*/HANDLE g_hEventCanRead,g_hEventCanWrite;/*g_hEventCanRead事件控制读者是否可读g_hEventCanWrite事件控制写着是否可写*/int readerCount;void WriterPrint(char *pstr){ EnterCriticalSection(&g_cs); printf("\t%s\n",pstr); LeaveCriticalSection(&g_cs);}unsigned int __stdcall WriterFun(PVOID PM){ WriterPrint("写者进入等待中。。。"); WaitForSingleObject(g_hEventCanWrite,INFINITE); //Sleep(1000); ResetEvent(g_hEventCanRead); WriterPrint("写者开始写文件。。。"); Sleep(100); WriterPrint("写者结束写文件。。。"); SetEvent(g_hEventCanRead); return 0;}void ReadPrint(DWORD ID,char *pstr){ EnterCriticalSection(&g_cs); printf("%d%s\n",ID,pstr); LeaveCriticalSection(&g_cs);}unsigned int __stdcall ReaderFun(PVOID PM){ ReadPrint(GetCurrentThreadId(),"号读者进入等待。。。"); WaitForSingleObject(g_hEventCanRead,INFINITE); EnterCriticalSection(&g_cs_reader_count); readerCount++; if(readerCount == 1) ResetEvent(g_hEventCanWrite); LeaveCriticalSection(&g_cs_reader_count); ReadPrint(GetCurrentThreadId(),"号读者开始阅读"); ReadPrint(GetCurrentThreadId(),"号读者结束阅读"); EnterCriticalSection(&g_cs_reader_count); readerCount--; if(readerCount == 0) SetEvent(g_hEventCanWrite); LeaveCriticalSection(&g_cs_reader_count); return 0;}int main(){ InitializeCriticalSection(&g_cs); InitializeCriticalSection(&g_cs_reader_count); //事件手动置位,初始为有信号TRUE g_hEventCanRead = CreateEvent(NULL,TRUE,TRUE,NULL); g_hEventCanWrite = CreateEvent(NULL,TRUE,TRUE,NULL); readerCount = 0; int i; HANDLE handle[READ_NUM+1]; for(i=1;i<=2;i++) handle[i] = (HANDLE)_beginthreadex(NULL,0,ReaderFun,NULL,0,NULL); handle[0] = (HANDLE)_beginthreadex(NULL,0,WriterFun,NULL,0,NULL); for(;i<=READ_NUM;i++) handle[i] = (HANDLE)_beginthreadex(NULL,0,ReaderFun,NULL,0,NULL); WaitForMultipleObjects(READ_NUM+1,handle,TRUE,INFINITE); for(i=0;i<=READ_NUM;i++) CloseHandle(handle[i]); CloseHandle(g_hEventCanRead); CloseHandle(g_hEventCanWrite); DeleteCriticalSection(&g_cs); DeleteCriticalSection(&g_cs_reader_count); return 0;}
结果截图:

从运行结果可以看到:读者与写着的操作时互斥的。

但是,接着阅读下面的游客评论,找出其中BUG,产生BUG的片段如下:

WriterPrint("写者进入等待中。。。");   WaitForSingleObject(g_hEventCanWrite,INFINITE);   ResetEvent(g_hEventCanRead);   WriterPrint("写者开始写文件。。。");   Sleep(100);   WriterPrint("写者结束写文件。。。");   SetEvent(g_hEventCanRead);
产生BUG的原因:写着在等待到可写事件(WaitForSingleObject(g_hEventCanWrite,INFINITE))的信号后,到置可读信号(ResetEvent(g-hEventCanRead))为未触发之前,读者线程可能获得权限,从而进行阅读的操作,但此时写者在等待到可写信号后,也在进行写作的操作,从而,没有保证读写的互斥。

将BUG片段稍做更改,即可体现:

unsigned int __stdcall WriterFun(PVOID PM){   WriterPrint("写者进入等待中。。。");   WaitForSingleObject(g_hEventCanWrite,INFINITE);   WriterPrint("写者开始写文件。。。");   Sleep(1000);   ResetEvent(g_hEventCanRead);   Sleep(100);   WriterPrint("写者结束写文件。。。");   SetEvent(g_hEventCanRead);   return 0;}
做如此更改,并没有改变程序的功能,但运行结果:

从结果看出,写者开始写文件到结束写文件之间,有读者在进行阅读,即程序没有实现读写的互斥。

是什么导致程序失去了读写的互斥性呢,个人看法是,没有保证读写的同步,个人认为只能先写后读,读者只有在写者的写作任务结束之后才能进行阅读,而程序1则使读者和写着一开始就都获得了可读和可写的权限,即在创建可读可写事件时,手动将事件设置为触发状态:

g_hEventCanRead = CreateEvent(NULL,TRUE,TRUE,NULL);g_hEventCanWrite  = CreateEvent(NULL,TRUE,TRUE,NULL);
这才导致了写者开始写作但还没来及通知读者不可以阅读之前,读者就进行了阅读,是程序失去了读写的互斥性,所以,在创建事件时,可读事件应该手动设置为未触发状态,而在写着完成写作之后,才将可读事件设置为触发状态,这就保证了读写的互斥性。但这只是一个写者和多个读者的解决办法。更好的办法,是如MoreWindows大神的第十四篇文章中一样进行解决。按照我所想的代码实现如下:

程序2:

#include 
#include
#include
const int READ_NUM = 5;CRITICAL_SECTION g_cs,g_cs_reader_count;/*g_cs用来确保对stdout输出端的操作完整性;g_cs_reader_count确保对readerCount计数的原子性*/HANDLE g_hEventCanRead,g_hEventCanWrite;/*g_hEventCanRead事件控制读者是否可读g_hEventCanWrite事件控制写着是否可写*/int readerCount;void WriterPrint(char *pstr){ EnterCriticalSection(&g_cs); printf("\t%s\n",pstr); LeaveCriticalSection(&g_cs);}unsigned int __stdcall WriterFun(PVOID PM){ WriterPrint("写者进入等待中。。。"); WaitForSingleObject(g_hEventCanWrite,INFINITE); WriterPrint("写者开始写文件。。。"); Sleep(1000); WriterPrint("写者结束写文件。。。"); SetEvent(g_hEventCanRead); return 0;}void ReadPrint(DWORD ID,char *pstr){ EnterCriticalSection(&g_cs); printf("%d%s\n",ID,pstr); LeaveCriticalSection(&g_cs);}unsigned int __stdcall ReaderFun(PVOID PM){ ReadPrint(GetCurrentThreadId(),"号读者进入等待。。。"); WaitForSingleObject(g_hEventCanRead,INFINITE); EnterCriticalSection(&g_cs_reader_count); readerCount++; if(readerCount == 1) ResetEvent(g_hEventCanWrite); LeaveCriticalSection(&g_cs_reader_count); ReadPrint(GetCurrentThreadId(),"号读者开始阅读"); ReadPrint(GetCurrentThreadId(),"号读者结束阅读"); EnterCriticalSection(&g_cs_reader_count); readerCount--; if(readerCount == 0) SetEvent(g_hEventCanWrite); LeaveCriticalSection(&g_cs_reader_count); return 0;}int main(){ InitializeCriticalSection(&g_cs); InitializeCriticalSection(&g_cs_reader_count); //事件手动置位,初始为有信号TRUE g_hEventCanRead = CreateEvent(NULL,TRUE,FALSE,NULL); g_hEventCanWrite = CreateEvent(NULL,TRUE,TRUE,NULL); readerCount = 0; int i; HANDLE handle[READ_NUM+1]; for(i=1;i<=2;i++) handle[i] = (HANDLE)_beginthreadex(NULL,0,ReaderFun,NULL,0,NULL); handle[0] = (HANDLE)_beginthreadex(NULL,0,WriterFun,NULL,0,NULL); for(;i<=READ_NUM;i++) handle[i] = (HANDLE)_beginthreadex(NULL,0,ReaderFun,NULL,0,NULL); WaitForMultipleObjects(READ_NUM+1,handle,TRUE,INFINITE); for(i=0;i<=READ_NUM;i++) CloseHandle(handle[i]); CloseHandle(g_hEventCanRead); CloseHandle(g_hEventCanWrite); DeleteCriticalSection(&g_cs); DeleteCriticalSection(&g_cs_reader_count); return 0;}
结果:

从结果看出,读者和写着之间的操作实现了互斥操作。

总结,以上两段程序的思想有一些出入,仅个人写下作为以后提醒复习用之。

你可能感兴趣的文章
Keras软件安装
查看>>
cuda安装
查看>>
Anaconda3换源配置
查看>>
Unsafe.putOrderedXXX系列方法详解(数组赋值的第二种方式)
查看>>
javase个人垃圾复习笔记05Java StringBuffer 和 StringBuilder 类
查看>>
牛客编程题(七)
查看>>
三种设计模式
查看>>
牛客编程题(八)
查看>>
牛客编程题(九)
查看>>
过滤流
查看>>
3.输入整型数组和排序标识,对其元素按照升序或降序进行排序
查看>>
13.找到字符串的最长无重复字符串字串
查看>>
java常用垃圾回收器G1和CMS有什么区别
查看>>
BIO、NIO,AIO的区别
查看>>
linux压缩与解压
查看>>
数据结构基础(一)
查看>>
Linux反弹shell姿势总结
查看>>
CVE-2018-2894 WebLogic远程上传漏洞复现
查看>>
Nginx解析漏洞复现
查看>>
GhostScript沙箱绕过(命令执行漏洞)CVE-2018-16509
查看>>