多线程面试题系列(16):多线程十大经典案例之一 双线程读写队列数据

前十五篇中介绍多线程的相关概念,多线程同步互斥问题(第四篇)及解决多线程同步互斥的常用方法——关键段事件互斥量信号量读写锁。为了让大家更加熟练运用多线程,将会有十篇文章来讲解十个多线程使用案例,相信看完这十篇后会让你能更加游刃有余的使用多线程。

首先来看第一篇——第十六篇 多线程十大经典案例之一 双线程读写队列数据

《多线程十大经典案例之一双线程读写队列数据》案例描述:

MFC对话框中一个按钮的响应函数实现两个功能:
显示数据同时处理数据,因此开两个线程,一个线程显示数据(开了一个定时器,响应WM_TIMER消息按照一定时间间隔向TeeChart图表添加数据并显示)同时在队列队尾添加数据,另一个线程从该队列队头去数据来处理。

下面就来解决这个案例。先来分析下。

《多线程十大经典案例之一双线程读写队列数据》案例分析:

这个案例是一个线程向队列中的队列头部读取数据,一个线程向队列中的队列尾部写入数据。看起来很像读者写者问题(见十一篇和十四篇),但其实不然,如果将队列看成缓冲区,这个案例明显是个生产者消费者问题(见第十篇)。因此我们仿照生产者消费者的思路来具体分析下案例中的“等待”情况:

1.     当队列为空时,读取数据线程必须等待写入数据向队列中写入数据。也就是说当队列为空时,读取数据线程要等待队列中有数据

2.     当队列满时,写入数据线程必须等待读取数据线程向队列中读取数据。也就是说当队列满时,写入数据线程要等待队列中有空位

在访问队列时,需要互斥吗?这将依赖于队列的数据结构实现,如果使用STL中的vector,由于vector会动态增长。因此要做互斥保护。如果使用循环队列,那么读取数据线程拥有读取指针,写入数据线程拥有写入指针,各自将访问队列中不同位置上的数据,因此不用进行互斥保护。

分析完毕后,再来考虑使用什么样的数据结构,同样依照第十篇中的做法。使用两个信号量,一个来记录循环队列中空位的个数,一个来记录循环队列中产品的个数(非空位个数)。代码非常容易写出,下面给出完整的源代码。

代码中的信号量相关函数可以参考第八篇,代码中的SetConsoleColor是用来改变控制台的文字颜色,具体可以参考《VC 控制台颜色设置》。

《多线程十大经典案例之一双线程读写队列数据》完整代码:

[cpp] view plain copy

  1. //第十六篇 多线程十大经典案例之一 双线程读写队列数据
  2. //http://blog.csdn.net/MoreWindows/article/details/8646902
  3. #include <stdio.h>
  4. #include <process.h>
  5. #include <windows.h>
  6. #include <time.h>
  7. const int QUEUE_LEN = 5;
  8. int g_arrDataQueue[QUEUE_LEN];
  9. int g_i, g_j, g_nDataNum;
  10. //关键段 用于保证互斥的在屏幕上输出
  11. CRITICAL_SECTION g_cs;
  12. //信号量 g_hEmpty表示队列中空位 g_hFull表示队列中非空位
  13. HANDLE     g_hEmpty, g_hFull;
  14. //设置控制台输出颜色
  15. BOOL SetConsoleColor(WORD wAttributes)
  16. {
  17. HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
  18. if (hConsole == INVALID_HANDLE_VALUE)
  19. return FALSE;
  20. return SetConsoleTextAttribute(hConsole, wAttributes);
  21. }
  22. //读数据线程函数
  23. unsigned int __stdcall ReaderThreadFun(PVOID pM)
  24. {
  25. int nData = 0;
  26. while (nData < 20)
  27. {
  28. WaitForSingleObject(g_hFull, INFINITE);
  29. nData = g_arrDataQueue[g_i];
  30. g_i = (g_i + 1) % QUEUE_LEN;
  31. EnterCriticalSection(&g_cs);
  32. printf("从队列中读数据%d\n", nData);
  33. LeaveCriticalSection(&g_cs);
  34. Sleep(rand() % 300);
  35. ReleaseSemaphore(g_hEmpty, 1, NULL);
  36. }
  37. return 0;
  38. }
  39. //写数据线程函数
  40. unsigned int __stdcall WriterThreadFun(PVOID pM)
  41. {
  42. int nData = 0;
  43. while (nData < 20)
  44. {
  45. WaitForSingleObject(g_hEmpty, INFINITE);
  46. g_arrDataQueue[g_j] = ++nData;
  47. g_j = (g_j + 1) % QUEUE_LEN;
  48. EnterCriticalSection(&g_cs);
  49. SetConsoleColor(FOREGROUND_GREEN);
  50. printf("    将数据%d写入队列\n", nData);
  51. SetConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
  52. LeaveCriticalSection(&g_cs);
  53. Sleep(rand() % 300);
  54. ReleaseSemaphore(g_hFull, 1, NULL);
  55. }
  56. return 0;
  57. }
  58. int main()
  59. {
  60. printf("     第十六篇 多线程十大经典案例 双线程读写队列数据\n");
  61. printf(" - by MoreWindows( http://blog.csdn.net/MoreWindows/article/details/8646902 ) -\n\n");
  62. InitializeCriticalSection(&g_cs);
  63. g_hEmpty = CreateSemaphore(NULL, QUEUE_LEN, QUEUE_LEN, NULL);
  64. g_hFull = CreateSemaphore(NULL, 0, QUEUE_LEN, NULL);
  65. srand(time(NULL));
  66. g_i = g_j = 0;
  67. HANDLE hThread[2];
  68. hThread[0] = (HANDLE)_beginthreadex(NULL, 0, ReaderThreadFun, NULL, 0, NULL);
  69. hThread[1] = (HANDLE)_beginthreadex(NULL, 0, WriterThreadFun, NULL, 0, NULL);
  70. WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
  71. for (int i = 0; i < 2; i++)
  72. CloseHandle(hThread[i]);
  73. CloseHandle(g_hEmpty);
  74. CloseHandle(g_hFull);
  75. DeleteCriticalSection(&g_cs);
  76. return 0;
  77. }

《多线程十大经典案例之一双线程读写队列数据》运行结果:

程序运行结果如下:

本文配套程序下载地址为:http://download.csdn.net/detail/morewindows/5136035

时间: 2024-10-10 00:12:01

多线程面试题系列(16):多线程十大经典案例之一 双线程读写队列数据的相关文章

多线程十大经典案例之一 双线程读写队列数据

本文配套程序下载地址为:http://download.csdn.net/detail/morewindows/5136035 转载请标明出处,原文地址:http://blog.csdn.net/morewindows/article/details/8646902 欢迎关注微博:http://weibo.com/MoreWindows 在<秒杀多线程系列>的前十五篇中介绍多线程的相关概念,多线程同步互斥问题<秒杀多线程第四篇一个经典的多线程同步问题>及解决多线程同步互斥的常用方法

秒杀多线程第十六篇 多线程十大经典案例之一 双线程读写队列数据

版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 本文配套程序下载地址为:http://download.csdn.net/detail/morewindows/5136035 转载请标明出处,原文地址:http://blog.csdn.net/morewindows/article/details/8646902 欢迎关注微博:http://weibo.com/MoreWindows 在<秒杀多线程系列>的前十五篇中介绍多线程的相关概念,多线程同步互斥问题<秒杀多

.NET面试题系列[16] - 多线程概念(1)

.NET面试题系列目录 这篇文章主要是各个百科中的一些摘抄,简述了进程和线程的来源,为什么出现了进程和线程. 操作系统层面中进程和线程的实现 操作系统发展史 直到20世纪50年代中期,还没出现操作系统,计算机工作采用手工操作方式.程序员将对应于程序和数据的已穿孔未的纸带(或卡片)装入输入机,然后启动输入机把程序和数据输入计算机内存,接着通过控制台开关启动程序针对数据运行:计算完毕,打印机输出计算结果:用户取走结果并卸下纸带(或卡片)后,才让下一个用户上机. 手工操作方式的两个特点: (1)用户独

多线程面试题系列(5):经典线程同步 关键段CS

上一篇提出了一个经典的多线程同步互斥问题,本篇将用关键段CRITICAL_SECTION来尝试解决这个问题.本文首先介绍下如何使用关键段,然后再深层次的分析下关键段的实现机制与原理.关键段CRITICAL_SECTION一共就四个函数,使用很是方便.下面是这四个函数的原型和使用说明. 函数功能:初始化 函数原型: void InitializeCriticalSection(LPCRITICAL_SECTIONlpCriticalSection); 函数说明:定义关键段变量后必须先初始化. 函数

秒杀多线程面试题系列

[ 专栏 ]- 秒杀多线程面试题系列 - MoreWindows Blog(格物穷理,以求自由!) - CSDN博客   PV原语操作详解 - Benson_xiong - 博客园   原文地址:https://www.cnblogs.com/zhehan54/p/10053582.html

数据挖掘十大经典算法

一. C4.5  C4.5算法是机器学习算法中的一种分类决策树算法,其核心算法是ID3 算法.   C4.5算法继承了ID3算法的优点,并在以下几方面对ID3算法进行了改进: 1) 用信息增益率来选择属性,克服了用信息增益选择属性时偏向选择取值多的属性的不足: 2) 在树构造过程中进行剪枝: 3) 能够完成对连续属性的离散化处理: 4) 能够对不完整数据进行处理. C4.5算法有如下优点:产生的分类规则易于理解,准确率较高.其缺点是:在构造树的过程中,需要对数据集进行多次的顺序扫描和排序,因而导

十大经典排序算法(python实现)(原创)

经典排序算法图解: 经典排序算法的复杂度: 大类一(比较排序法): 1.冒泡排序(Bubble Sort) python代码实现: 1 d0 = [2, 15, 5, 9, 7, 6, 4, 12, 5, 4, 2, 64, 5, 6, 4, 2, 3, 54, 45, 4, 44] 2 d0_out = [2, 2, 2, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 7, 9, 12, 15, 44, 45, 54, 64] # 正确排序 3 4 while 1: 5 stat

十大经典排序算法(Python,Java实现)

参照:https://www.cnblogs.com/wuxinyan/p/8615127.html https://www.cnblogs.com/onepixel/articles/7674659.html 一.排序算法分类: 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序. 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排 二.算法复杂度 注(

【转】十大经典排序算法

[转]十大经典排序算法:https://www.cnblogs.com/onepixel/articles/7674659.html 0.算法概述 0.1 算法分类 十种常见排序算法可以分为两大类: 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序. 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序. 0.2 算法复杂度 0.3 相关概念 稳定:如果