在第十一篇文章中我们使用事件和一个记录读者个数的变量来解决读者写者问题。问题虽然得到了解决,但代码有点复杂。本篇将介绍一种新方法——读写锁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. ubuntu下apache2-php-mysql的环境配置

    基本的支持环境.暂时还不应用zend优化,因此这里就不涉及到zend optimizer的安装了.其实在ubuntu系统中中安装远比在windows系统中设置更为容易,而且在终端下设置更省事. 1.安 ...

  2. RQNOJ 201 奥运大包围:LIS + 拼链成环

    题目链接:https://www.rqnoj.cn/problem/201 题意: 开始时n(n<=1000)个人手拉手围成一个圈. 后来这些人中的一些按顺序向里面出圈形成一个新圈.从而使原圈形 ...

  3. MySQL show 语句

    总览show 语句 show tables from database_name; -- 显示当前数据库中所有表的名称.  show databases; -- 显示mysql中所有数据库的名称.  ...

  4. 怎样给Win7系统设置一个默认的浏览器

    怎样给Win7系统设置一个默认的浏览器.. --------------- 点击桌面左下角「windows图标」,再点击「控制面板」,如图: 点击「程序」,如图: 点击「设置默认程序」,如图: 等到「 ...

  5. 你以为你真的会用编辑器----之Vim

    Vim ----------------------- Vim下载地址:http://www.vim.org/download.php -------------------------------- ...

  6. JavaScript 学习笔记 - LocalStorage

    前言 本文主要介绍本地存储的基本使用,以及它和 Cookie.SessionStorage 的区别. 简单回顾 Cookie 在 HTML5 之前,本地存储数据一般是通过 Cookie 来完成的.我们 ...

  7. 横排列表(li)的左右对齐

    效果如下: HTML代码如下: <div class="overdiv"> <ul> <li>1</li> <li>2& ...

  8. 关于JDBC导入mysql的jar驱动的头痛

    今天上午想写个小程序,需要调用数据库,查了书和各个博客. 最后卡在导入mysql驱动上了,花了1个多小时才让程序连上数据库. 这里有个小误区,你下载的是zip压缩文件,很多帖子写的都是让你导入驱动,但 ...

  9. vue.js基础知识篇(1):简介、数据绑定

    目录第一章:vue.js是什么? 第二章:数据绑定第三章:指令第四章:计算属性第五章:表单控件绑定代码链接: http://pan.baidu.com/s/1qXCfzRI 密码: 5j79 第一章: ...

  10. koa/redux middleware系统解析

    middleware 对于现有的一些框架比如koa,express,redux,都需要对数据流进行一些处理,比如koa,express的请求数据处理,包括json.stringify,logger,或 ...