多线程面试题系列(14):读者写者问题继 读写锁SRWLock
在第十一篇文章中我们使用事件和一个记录读者个数的变量来解决读者写者问题。问题虽然得到了解决,但代码有点复杂。本篇将介绍一种新方法——读写锁SRWLock来解决这一问题。读写锁在对资源进行保护的同时,还能区分想要读取资源值的线程(读取者线程)和想要更新资源的线程(写入者线程)。对于读取者线程,读写锁会允许他们并发的执行。当有写入者线程在占有资源时,读写锁会让其它写入者线程和读取者线程等待。因此用读写锁来解决读者写者问题会使代码非常清晰和简洁。
下面就来看看如何使用读写锁,要注意编译读写锁程序需要VS2008,运行读写锁程序要在Vista或Windows Server2008系统(比这两个更高级的系统也可以)。读写锁的主要函数就五个,分为初始化函数,写入者线程申请和释放函数,读取者线程申请和释放函数,以下是详细的函数使用说明:
第一个 InitializeSRWLock
函数功能:初始化读写锁
函数原型:VOID InitializeSRWLock(PSRWLOCK SRWLock);
函数说明:初始化(没有删除或销毁SRWLOCK的函数,系统会自动清理)
第二个 AcquireSRWLockExclusive
函数功能:写入者线程申请写资源。
函数原型:VOID AcquireSRWLockExclusive(PSRWLOCK SRWLock);
第三个 ReleaseSRWLockExclusive
函数功能:写入者线程写资源完毕,释放对资源的占用。
函数原型:VOID ReleaseSRWLockExclusive(PSRWLOCK SRWLock);
第四个 AcquireSRWLockShared
函数功能:读取者线程申请读资源。
函数原型:VOID AcquireSRWLockShared(PSRWLOCK SRWLock);
第五个 ReleaseSRWLockShared
函数功能:读取者线程结束读取资源,释放对资源的占用。
函数原型:VOID ReleaseSRWLockShared(PSRWLOCK SRWLock);
注意一个线程仅能锁定资源一次,不能多次锁定资源。
使用读写锁精简后的代码如下(代码中变参函数的实现请参阅《C,C++中使用可变参数》,控制台颜色设置请参阅《VC 控制台颜色设置》):
- //读者与写者问题继 读写锁SRWLock
- #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;
- SRWLOCK g_srwLock;
- //读者线程输出函数(变参函数的实现)
- 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());
- //读者申请读取文件
- AcquireSRWLockShared(&g_srwLock);
- //读取文件
- ReaderPrintf("编号为%d的读者开始读取文件...\n", GetCurrentThreadId());
- Sleep(rand() % 100);
- ReaderPrintf(" 编号为%d的读者结束读取文件\n", GetCurrentThreadId());
- //读者结束读取文件
- ReleaseSRWLockShared(&g_srwLock);
- 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("写者线程进入等待中...");
- //写者申请写文件
- AcquireSRWLockExclusive(&g_srwLock);
- //写文件
- WriterPrintf(" 写者开始写文件.....");
- Sleep(rand() % 100);
- WriterPrintf(" 写者结束写文件");
- //标记写者结束写文件
- ReleaseSRWLockExclusive(&g_srwLock);
- return 0;
- }
- int main()
- {
- printf(" 读者写者问题继 读写锁SRWLock\n");
- printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");
- //初始化读写锁和关键段
- InitializeCriticalSection(&g_cs);
- InitializeSRWLock(&g_srwLock);
- HANDLE hThread[READER_NUM + 1];
- int i;
- //先启动二个读者线程
- 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]);
- //销毁关键段
- DeleteCriticalSection(&g_cs);
- return 0;
- }
对比下第十一篇中的代码就可以发现这份代码确实清爽许多了。这个程序用VS2008编译可以通过,但在XP系统下运行会导致报错。
在Win7系统下能够正确的运行,结果如图所示:
最后总结一下读写锁SRWLock
1.读写锁声明后要初始化,但不用销毁,系统会自动清理读写锁。
2.读取者和写入者分别调用不同的申请函数和释放函数。
多线程面试题系列(14):读者写者问题继 读写锁SRWLock的更多相关文章
- 转---秒杀多线程第十四篇 读者写者问题继 读写锁SRWLock
在<秒杀多线程第十一篇读者写者问题>文章中我们使用事件和一个记录读者个数的变量来解决读者写者问题.问题虽然得到了解决,但代码有点复杂.本篇将介绍一种新方法——读写锁SRWLock来解决这一 ...
- 秒杀多线程第十四篇 读者写者问题继 读写锁SRWLock (续)
java 包实现了读写锁的操作: package com.multithread.readwritelock; import java.util.concurrent.CountDownLatch; ...
- 读者写者问题继 读写锁SRWLock
在<秒杀多线程第十一篇读者写者问题>文章中我们使用事件和一个记录读者个数的变量来解决读者写者问题.问题虽然得到了解决,但代码有点复杂.本篇将介绍一种新方法--读写锁SRWLock来解决这一 ...
- 多线程 读写锁SRWLock
在<秒杀多线程第十一篇读者写者问题>文章中我们使用事件和一个记录读者个数的变量来解决读者写者问题.问题虽然得到了解决,但代码有点复杂.本篇将介绍一种新方法——读写锁SRWLock来解决这一 ...
- .NET面试题系列[14] - LINQ to SQL与IQueryable
.NET面试题系列目录 名言警句 "理解IQueryable的最简单方式就是,把它看作一个查询,在执行的时候,将会生成结果序列." - Jon Skeet LINQ to Obje ...
- 多线程面试题系列(3):原子操作 Interlocked系列函数
上一篇中讲到一个多线程报数功能.为了描述方便和代码简洁起见,我们可以只输出最后的报数结果来观察程序是否运行出错.这也非常类似于统计一个网站每天有多少用户登录,每个用户登录用一个线程模拟,线程运行时会将 ...
- 多线程面试题系列(12):多线程同步内功心法——PV操作上
上面的文章讲解了在Windows系统下实现多线程同步互斥的方法,为了提高在实际问题中分析和思考多个线程之间同步互斥问题的能力,接下来将讲解PV操作,这也是操作系统中的重点和难点.本文将会先简要介绍下P ...
- 多线程面试题系列(8):经典线程同步 信号量Semaphore
前面介绍了关键段CS.事件Event.互斥量Mutex在经典线程同步问题中的使用.本篇介绍用信号量Semaphore来解决这个问题. 首先也来看看如何使用信号量,信号量Semaphore常用有三个函数 ...
- 多线程面试题系列(7):经典线程同步 互斥量Mutex
前面介绍了关键段CS.事件Event在经典线程同步问题中的使用.本篇介绍用互斥量Mutex来解决这个问题. 互斥量也是一个内核对象,它用来确保一个线程独占一个资源的访问.互斥量与关键段的行为非常相似, ...
随机推荐
- C++中printf和scanf的用法
(一)printf的用法 printf:按格式打印,向控制台输出.print:打印 ,f:formate,格式化. 在使用printf向控制台输出时,不建议使用中文字符串,中文字符串的问题比较复杂,有 ...
- decimal扩展方法(转换为字符串,去掉末尾的0)
/// <summary> /// 转换为字符串,去掉末尾0 /// </summary> /// <param name="target">被 ...
- 新瓶装旧酒:全程无命令 GitHub Pages 创建您的博客站点
使用 GitHub Pages 创建博客站点的文章很多,也有很长的历史了.但是,许多已经与当前的 GitHub 不一致了,如果你按图索骥,会发现驴唇对不上马嘴. 更为麻烦的是,你会发现或者需要你输入许 ...
- LAMP环境的搭建(二)----Mysql安装与配置
Mysql是一款免费开源的关系型数据库. 尽管Mysql被oracle公司收购.但是oracle并没有限制了mysql的发展. 目前mysql是使用范围最广的关系型数据库,许多大型公司也在使用.截至本 ...
- [js高手之路]设计模式系列课程-发布者,订阅者重构购物车
发布者订阅者模式,是一种很常见的模式,比如: 一.买卖房子 生活中的买房,卖房,中介就构成了一个发布订阅者模式,买房的人,一般需要的是房源,价格,使用面积等信息,他充当了订阅者的角色 中介拿到卖主的房 ...
- 为shell布置陷阱:trap捕捉信号方法论
本文目录: 1.1 信号说明 1.2 trap布置陷阱 1.3 布置完美陷阱必备知识 家里有老鼠,快消灭它!哎,又给跑了.老鼠这小东西跑那么快,想直接直接消灭它还真不那么容易.于是,老鼠药.老鼠夹子或 ...
- python+selenium自动化软件测试(第4章):场景判断与封装
4.1 显示等待WebDriverWait 前言:在脚本中加入太多的sleep后会影响脚本的执行速度,虽然implicitly_wait()这种隐式等待在一定程度上节省了很多时间.但是一旦页面上某些j ...
- hr用法
定义和用法 <hr> 标签在 HTML 页面中创建一条水平线. 水平分隔线(horizontal rule)可以在视觉上将文档分隔成各个部分. HTML 与 XHTML 之间的差异 在 H ...
- https 协议下服务器根据网络地址下载上传文件问题
https 协议下服务器根据网络地址下载上传文件遇到(PKIX:unable to find valid certification path to requested target 的问题) 使用h ...
- java 读取json
http://blog.csdn.net/5iasp/article/details/38545875 private void readAccount() throws IOException { ...