在第十一篇文章中我们使用事件和一个记录读者个数的变量来解决读者写者问题。问题虽然得到了解决,但代码有点复杂。本篇将介绍一种新方法——读写锁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 控制台颜色设置》):

  1. //读者与写者问题继 读写锁SRWLock
  2. #include <stdio.h>
  3. #include <process.h>
  4. #include <windows.h>
  5. //设置控制台输出颜色
  6. BOOL SetConsoleColor(WORD wAttributes)
  7. {
  8. HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
  9. if (hConsole == INVALID_HANDLE_VALUE)
  10. return FALSE;
  11. return SetConsoleTextAttribute(hConsole, wAttributes);
  12. }
  13. const int READER_NUM = 5;  //读者个数
  14. //关键段和事件
  15. CRITICAL_SECTION g_cs;
  16. SRWLOCK          g_srwLock;
  17. //读者线程输出函数(变参函数的实现)
  18. void ReaderPrintf(char *pszFormat, ...)
  19. {
  20. va_list   pArgList;
  21. va_start(pArgList, pszFormat);
  22. EnterCriticalSection(&g_cs);
  23. vfprintf(stdout, pszFormat, pArgList);
  24. LeaveCriticalSection(&g_cs);
  25. va_end(pArgList);
  26. }
  27. //读者线程函数
  28. unsigned int __stdcall ReaderThreadFun(PVOID pM)
  29. {
  30. ReaderPrintf("     编号为%d的读者进入等待中...\n", GetCurrentThreadId());
  31. //读者申请读取文件
  32. AcquireSRWLockShared(&g_srwLock);
  33. //读取文件
  34. ReaderPrintf("编号为%d的读者开始读取文件...\n", GetCurrentThreadId());
  35. Sleep(rand() % 100);
  36. ReaderPrintf(" 编号为%d的读者结束读取文件\n", GetCurrentThreadId());
  37. //读者结束读取文件
  38. ReleaseSRWLockShared(&g_srwLock);
  39. return 0;
  40. }
  41. //写者线程输出函数
  42. void WriterPrintf(char *pszStr)
  43. {
  44. EnterCriticalSection(&g_cs);
  45. SetConsoleColor(FOREGROUND_GREEN);
  46. printf("     %s\n", pszStr);
  47. SetConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
  48. LeaveCriticalSection(&g_cs);
  49. }
  50. //写者线程函数
  51. unsigned int __stdcall WriterThreadFun(PVOID pM)
  52. {
  53. WriterPrintf("写者线程进入等待中...");
  54. //写者申请写文件
  55. AcquireSRWLockExclusive(&g_srwLock);
  56. //写文件
  57. WriterPrintf("  写者开始写文件.....");
  58. Sleep(rand() % 100);
  59. WriterPrintf("  写者结束写文件");
  60. //标记写者结束写文件
  61. ReleaseSRWLockExclusive(&g_srwLock);
  62. return 0;
  63. }
  64. int main()
  65. {
  66. printf("  读者写者问题继 读写锁SRWLock\n");
  67. printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");
  68. //初始化读写锁和关键段
  69. InitializeCriticalSection(&g_cs);
  70. InitializeSRWLock(&g_srwLock);
  71. HANDLE hThread[READER_NUM + 1];
  72. int i;
  73. //先启动二个读者线程
  74. for (i = 1; i <= 2; i++)
  75. hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ReaderThreadFun, NULL, 0, NULL);
  76. //启动写者线程
  77. hThread[0] = (HANDLE)_beginthreadex(NULL, 0, WriterThreadFun, NULL, 0, NULL);
  78. Sleep(50);
  79. //最后启动其它读者结程
  80. for ( ; i <= READER_NUM; i++)
  81. hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ReaderThreadFun, NULL, 0, NULL);
  82. WaitForMultipleObjects(READER_NUM + 1, hThread, TRUE, INFINITE);
  83. for (i = 0; i < READER_NUM + 1; i++)
  84. CloseHandle(hThread[i]);
  85. //销毁关键段
  86. DeleteCriticalSection(&g_cs);
  87. return 0;
  88. }

对比下第十一篇中的代码就可以发现这份代码确实清爽许多了。这个程序用VS2008编译可以通过,但在XP系统下运行会导致报错。

在Win7系统下能够正确的运行,结果如图所示:

最后总结一下读写锁SRWLock

1.读写锁声明后要初始化,但不用销毁,系统会自动清理读写锁。

2.读取者和写入者分别调用不同的申请函数和释放函数。

多线程面试题系列(14):读者写者问题继 读写锁SRWLock的更多相关文章

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

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

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

    java 包实现了读写锁的操作: package com.multithread.readwritelock; import java.util.concurrent.CountDownLatch; ...

  3. 读者写者问题继 读写锁SRWLock

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

  4. 多线程 读写锁SRWLock

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

  5. .NET面试题系列[14] - LINQ to SQL与IQueryable

    .NET面试题系列目录 名言警句 "理解IQueryable的最简单方式就是,把它看作一个查询,在执行的时候,将会生成结果序列." - Jon Skeet LINQ to Obje ...

  6. 多线程面试题系列(3):原子操作 Interlocked系列函数

    上一篇中讲到一个多线程报数功能.为了描述方便和代码简洁起见,我们可以只输出最后的报数结果来观察程序是否运行出错.这也非常类似于统计一个网站每天有多少用户登录,每个用户登录用一个线程模拟,线程运行时会将 ...

  7. 多线程面试题系列(12):多线程同步内功心法——PV操作上

    上面的文章讲解了在Windows系统下实现多线程同步互斥的方法,为了提高在实际问题中分析和思考多个线程之间同步互斥问题的能力,接下来将讲解PV操作,这也是操作系统中的重点和难点.本文将会先简要介绍下P ...

  8. 多线程面试题系列(8):经典线程同步 信号量Semaphore

    前面介绍了关键段CS.事件Event.互斥量Mutex在经典线程同步问题中的使用.本篇介绍用信号量Semaphore来解决这个问题. 首先也来看看如何使用信号量,信号量Semaphore常用有三个函数 ...

  9. 多线程面试题系列(7):经典线程同步 互斥量Mutex

    前面介绍了关键段CS.事件Event在经典线程同步问题中的使用.本篇介绍用互斥量Mutex来解决这个问题. 互斥量也是一个内核对象,它用来确保一个线程独占一个资源的访问.互斥量与关键段的行为非常相似, ...

随机推荐

  1. 再起航,我的学习笔记之JavaScript设计模式10(单例模式)

    单例模式 单例模式(Singleton) : 又被称为单体模式,是只允许实例化一次的对象类.一个类有且仅有一个实例,并且自行实例化向整个系统提供. 命名空间 单例模式可能是JavaScript中我们最 ...

  2. Hibernate2---- 查询单条记录

    1.根据已完成的Hibernate1基础案例,我们接下来写一下查询,修改删除,对于基础生可以学习一下 只改写一下测试类的代码 private void findStudent() { //02Hibe ...

  3. WebApp开发总结

    WebApp开发总结 框架的使用网络上都有教程,就不写了,主要记录下个人的开发总结以方便以后开发注意. css公用样式统一定义 css样式抽出复用 appearance: none; 取消系统默认样式 ...

  4. RoutePrefix和Route 路由前缀

    使用应用到某个控制器中所有操作的路由前缀来批注该控制器. web api /// <summary> ////// </summary> [RoutePrefix(" ...

  5. vue开发项目的坑

    [Vue warn]: Do not use built-in or reserved HTML elements as component id: MenuItem [Vue warn]: Do n ...

  6. Apache Storm 1.1.0 中文文档 | ApacheCN

    前言 Apache Storm 是一个免费的,开源的,分布式的实时计算系统. 官方文档: http://storm.apache.org 中文文档: http://storm.apachecn.org ...

  7. @media实现网页自适应中的几个关键分辨率

    不同分辨率设备或不同窗口大小下网页布局经常是不同的,一不小心就会发生错位.可以利用@media screen实现网页布局的自适应,但是怎样兼容所有主流设备就成了问题.到底分辨率是多少的时候设置呢?首先 ...

  8. Java并发编程-阻塞队列

    Java concurrent 包中BlockingQueue接口有ArrayBlockingqueue.LinkedBlockingQueue.PriorityBlockingQueue.Synch ...

  9. amoeba

    Amoeba 原理:amoeba相当于业务员,处理client的读写请求,并将读写请求分开处理.amoeba和master以及slave都有联系,如果是读的请求,amoeba就从slave读取信息反馈 ...

  10. Ext:添加进度条

    var myMask = new Ext.LoadMask(Ext.getBody(), {msg:"正在提交..."}); myMask.show(); myMask.hide( ...