与上一篇《秒杀多线程第十篇 生产者消费者问题》的生产者消费者问题一样,读者写者也是一个非常著名的同步问题。读者写者问题描述非常简单,有一个写者很多读者,多个读者可以同时读文件,但写者在写文件时不允许有读者在读文件,同样有读者在读文件时写者也不去能写文件。

上面是读者写者问题示意图,类似于生产者消费者问题的分析过程,首先来找找哪些是属于“等待”情况。

第一.写者要等到没有读者时才能去写文件。

第二.所有读者要等待写者完成写文件后才能去读文件。

找完“等待”情况后,再看看有没有要互斥访问的资源。由于只有一个写者而读者们是可以共享的读文件,所以按题目要求并没有需要互斥访问的资源。类似于上一篇中美观的彩色输出,我们对生产者输出代码进行了颜色设置(在控制台输出颜色设置参见《VC 控制台颜色设置》)。因此在这里要加个互斥访问,不然很有可能在写者线程将控制台颜色设置还原之前,读者线程就已经有输出了。所以要对输出语句作个互斥访问处理,修改后的读者及写者的输出函数如下所示:

//读者线程输出函数
void ReaderPrintf(char *pszFormat, ...)
{va_list   pArgList;va_start(pArgList, pszFormat);EnterCriticalSection(&g_cs);vfprintf(stdout, pszFormat, pArgList);LeaveCriticalSection(&g_cs);va_end(pArgList);
}
//写者线程输出函数
void WriterPrintf(char *pszStr)
{EnterCriticalSection(&g_cs);SetConsoleColor(FOREGROUND_GREEN);printf("     %s\n", pszStr);SetConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);LeaveCriticalSection(&g_cs);
}

读者线程输出函数所使用的可变参数详见《C,C++中使用可变参数》。

解决了互斥输出问题,接下来再考虑如何实现同步问题可以设置一个变量来记录正在读文件的读者个数第一个开始读文件的读者要负责将关闭允许写者进入的标志,最后一个结束读文件的读者要负责打开允许写者进入的标志!!!!!。这样第一种“等待”情况就解决了。第二种“等待”情况是有写者进入时所以读者不能进入,使用一个事件就可以完成这个任务了——所有读者都要等待这个事件而写者负责触发事件和设置事件为未触发!!!!。详细见代码中注释:

//读者与写者问题
#include <stdio.h>
#include <process.h>
#include <windows.h>
//设置控制台输出颜色
BOOL SetConsoleColor(WORD wAttributes)
{HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);if (hConsole == INVALID_HANDLE_VALUE)return FALSE;return SetConsoleTextAttribute(hConsole, wAttributes);
}
const int READER_NUM = 5;  //读者个数
//关键段和事件
CRITICAL_SECTION g_cs, g_cs_writer_count;
HANDLE g_hEventWriter, g_hEventNoReader;
int g_nReaderCount;
//读者线程输出函数(变参函数的实现)
void ReaderPrintf(char *pszFormat, ...)
{va_list   pArgList;va_start(pArgList, pszFormat);EnterCriticalSection(&g_cs);vfprintf(stdout, pszFormat, pArgList);LeaveCriticalSection(&g_cs);va_end(pArgList);
}
//读者线程函数
unsigned int __stdcall ReaderThreadFun(PVOID pM)
{ReaderPrintf("     编号为%d的读者进入等待中...\n", GetCurrentThreadId());//等待写者完成WaitForSingleObject(g_hEventWriter, INFINITE);//读者个数增加EnterCriticalSection(&g_cs_writer_count);g_nReaderCount++;if (g_nReaderCount == 1)ResetEvent(g_hEventNoReader);LeaveCriticalSection(&g_cs_writer_count);//读取文件ReaderPrintf("编号为%d的读者开始读取文件...\n", GetCurrentThreadId());Sleep(rand() % 100);//结束阅读,读者个数减小,空位增加ReaderPrintf(" 编号为%d的读者结束读取文件\n", GetCurrentThreadId());//读者个数减少EnterCriticalSection(&g_cs_writer_count);g_nReaderCount--;if (g_nReaderCount == 0)SetEvent(g_hEventNoReader);LeaveCriticalSection(&g_cs_writer_count);return 0;
}
//写者线程输出函数
void WriterPrintf(char *pszStr)
{EnterCriticalSection(&g_cs);SetConsoleColor(FOREGROUND_GREEN);printf("     %s\n", pszStr);SetConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);LeaveCriticalSection(&g_cs);
}
//写者线程函数
unsigned int __stdcall WriterThreadFun(PVOID pM)
{WriterPrintf("写者线程进入等待中...");//等待读文件的读者为零WaitForSingleObject(g_hEventNoReader, INFINITE);//标记写者正在写文件ResetEvent(g_hEventWriter);//写文件WriterPrintf("  写者开始写文件.....");Sleep(rand() % 100);WriterPrintf("  写者结束写文件");//标记写者结束写文件SetEvent(g_hEventWriter);return 0;
}
int main()
{printf("  读者写者问题\n");printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");//初始化事件和信号量InitializeCriticalSection(&g_cs);InitializeCriticalSection(&g_cs_writer_count);//手动置位,初始已触发g_hEventWriter = CreateEvent(NULL, TRUE, TRUE, NULL);g_hEventNoReader  = CreateEvent(NULL, FALSE, TRUE, NULL);g_nReaderCount = 0;int i;HANDLE hThread[READER_NUM + 1];//先启动二个读者线程for (i = 1; i <= 2; i++)hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ReaderThreadFun, NULL, 0, NULL);//启动写者线程hThread[0] = (HANDLE)_beginthreadex(NULL, 0, WriterThreadFun, NULL, 0, NULL);Sleep(50);//最后启动其它读者结程for ( ; i <= READER_NUM; i++)hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ReaderThreadFun, NULL, 0, NULL);WaitForMultipleObjects(READER_NUM + 1, hThread, TRUE, INFINITE);for (i = 0; i < READER_NUM + 1; i++)CloseHandle(hThread[i]);//销毁事件和信号量CloseHandle(g_hEventWriter);CloseHandle(g_hEventNoReader);DeleteCriticalSection(&g_cs);DeleteCriticalSection(&g_cs_writer_count);return 0;
}

结果:

根据结果可以看出当有读者在读文件时,写者线程会进入等待状态中。当写者线程在写文件时,读者线程也会排队等待,说明读者和写者已经完成了同步。

本系列通过经典线程同步问题来列举线程同步手段的关键段、事件、互斥量、信号量,并作对这四种方法进行了总结。然后又通过二个著名的线程同步实例——生产者消费者问题和读者写者问题来强化对多线程同步互斥的理解与运用。希望读者们能够熟练掌握,从而在笔试面试中能够顺利的“秒杀”多线程的相关试题,获得自己满意的offer。

从《秒杀多线程第十篇生产者消费者问题》到《秒杀多线程第十一篇读者写者问题》可以得出多线程问题的关键在于找到所有“等待”情况和判断有无需要互斥访问的资源。那么如何从实际问题中更好更快更全面的找出这些了?请看《秒杀多线程第十二篇多线程同步内功心法——PV操作上》和《秒杀多线程第十三篇多线程同步内功心法——PV操作下》这二篇以加强解决多线程同步问题的“内功”。

另外,读者写者问题可以用读写锁SRWLock来解决,请看《秒杀多线程第十四篇 读者写者问题继 读写锁SRWLock》

转载请标明出处,原文地址:http://blog.csdn.net/morewindows/article/details/7596034

秒杀多线程第十一篇---读者写者问题相关推荐

  1. 秒杀多线程第十一篇 读者写者问题

    与上一篇<秒杀多线程第十篇 生产者消费者问题>的生产者消费者问题一样,读者写者也是一个非常著名的同步问题.读者写者问题描述非常简单,有一个写者很多读者,多个读者可以同时读文件,但写者在写文 ...

  2. 秒杀多线程第十四篇 读者写者问题继 读写锁SRWLock

    在<秒杀多线程第十一篇读者写者问题>文章中我们使用事件和一个记录读者个数的变量来解决读者写者问题.问题虽然得到了解决,但代码有点复杂.本篇将介绍一种新方法--读写锁SRWLock来解决这一 ...

  3. 秒杀多线程第十篇 生产者消费者问题

    继经典线程同步问题之后,我们来看看生产者消费者问题及读者写者问题.生产者消费者问题是一个著名的线程同步问题,该问题描述如下:有一个生产者在生产产品,这些产品将提供给若干个消费者去消费,为了使生产者和消 ...

  4. 秒杀多线程第六篇 经典线程同步 事件Event

    阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇 一个经典的多线程同步问题> <秒杀多线程第五篇 经典线程同步关键段CS> 上一篇中使用关键段来解决经典的多线程同步互斥问题 ...

  5. 秒杀多线程第七篇 经典线程同步 互斥量Mutex

    阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇一个经典的多线程同步问题> <秒杀多线程第五篇经典线程同步关键段CS> <秒杀多线程第六篇经典线程同步事件Event& ...

  6. 秒杀多线程第八篇 经典线程同步 信号量Semaphore

    阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇一个经典的多线程同步问题> <秒杀多线程第五篇经典线程同步关键段CS> <秒杀多线程第六篇经典线程同步事件Event& ...

  7. 秒杀多线程第五篇 经典线程同步 关键段CS

    上一篇<秒杀多线程第四篇 一个经典的多线程同步问题>提出了一个经典的多线程同步互斥问题,本篇将用关键段CRITICAL_SECTION来尝试解决这个问题. 本文首先介绍下如何使用关键段,然 ...

  8. 秒杀多线程第三篇 原子操作 Interlocked系列函数

    上一篇<多线程第一次亲密接触 CreateThread与_beginthreadex本质区别>中讲到一个多线程报数功能.为了描述方便和代码简洁起见,我们可以只输出最后的报数结果来观察程序是 ...

  9. 秒杀多线程第十篇 生产者消费者问题 (续)

    使用java 和semaphore实现的 ,多个生产者和多个消费者的问题. 1.使用Semaphore,Semaphore的大小设定为BUFFER_LENGTH.也就是同时最多有这么多线程来操作缓冲区 ...

最新文章

  1. linux 自动化交互套件 expect 介绍 shell非交互
  2. 3、4TP之url和路由
  3. linear model课程笔记
  4. 18行代码解决:(C语言)L1-046 整除光棍 (20分)
  5. 【渝粤教育】 国家开放大学2020年春季 1021劳动与社会保障法 参考试题
  6. VSCode 如何新建vue模板 - 插件引入篇
  7. Linux系统文件夹权限475,linux系统中文件的特殊权限
  8. 利用python进行tf-idf算法绘制词云图_利用python实现通过TF-IDF和BM25提取文章关键词...
  9. GO语言实现设计模式【全】
  10. Java线程中断理解(interrupte)
  11. 2021概率论与数理统计辅导讲义-余丙森
  12. java下载网络文件的几种方式
  13. 计算机应用基础教案 电子书,计算机应用基础教案(全套)-20210511075659.pdf-原创力文档...
  14. 今天电脑突然出现问题: 请安装 TCP/IP 协议 错误 10106 【已解决·】
  15. Mybatis出现Mapped Statements collection already contains value for
  16. 优秀的人是如何通过互联网赚钱的,4个案例告诉你
  17. 【Spring】context:component-scan包扫描问题
  18. 多张照片怎么添加时间水印
  19. 机房监控解析大全都在这里!
  20. tkinter Canvas 实现 鼠标手绘画板 功能

热门文章

  1. WindowsServer操作系统安全
  2. i7+GTX1660Ti电脑安装PaddlePaddle-GPU
  3. flutter 路由守卫
  4. 3Dmax和Maya孰优孰劣?初学者入门该如何选择?
  5. 2017.11.29_实验8_java基本类库
  6. [Python] cmd中‘py‘命令不被识别的解决方案
  7. 如何衡量员工的积极性以提高绩效
  8. 亿级音乐服务容器化实践
  9. delphi 编码转换 unicode gbk big5
  10. IOS 系统自带的分享功能之 UIActivityViewController