热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

本文配套程序下载地址为:http:download.csdn.netdetailmorewindows5136035转载请标明出处,原文地址:http:blog.csdn.netm

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

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

欢迎关注微博:http://weibo.com/MoreWindows

 

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

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

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

本案例来源于http://bbs.csdn.NET/topics/390383114,感谢hehening88提供题目,特此鸣谢。

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

 

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

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

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

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

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

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

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

 

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

[cpp] view plain copy
 
  1. //秒杀多线程第十六篇 多线程十大经典案例之一 双线程读写队列数据  
  2. //http://blog.csdn.net/MoreWindows/article/details/8646902  
  3. #include   
  4. #include   
  5. #include   
  6. #include   
  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;                            //注意这里   nData++; 和 ++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.       
  63.     InitializeCriticalSection(&g_cs);  
  64.     g_hEmpty = CreateSemaphore(NULL, QUEUE_LEN, QUEUE_LEN, NULL);  
  65.     g_hFull = CreateSemaphore(NULL, 0, QUEUE_LEN, NULL);  
  66.       
  67.     srand(time(NULL));  
  68.     g_i = g_j = 0;  
  69.     HANDLE hThread[2];  
  70.     hThread[0] = (HANDLE)_beginthreadex(NULL, 0, ReaderThreadFun, NULL, 0, NULL);  
  71.     hThread[1] = (HANDLE)_beginthreadex(NULL, 0, WriterThreadFun, NULL, 0, NULL);  
  72.       
  73.     WaitForMultipleObjects(2, hThread, TRUE, INFINITE);  
  74.       
  75.     for (int i = 0; i < 2; i++)  
  76.         CloseHandle(hThread[i]);  
  77.     CloseHandle(g_hEmpty);  
  78.     CloseHandle(g_hFull);  
  79.     DeleteCriticalSection(&g_cs);  
  80.     return 0;  
  81. }  

 

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

程序运行结果如下:

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

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

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

欢迎关注微博:http://weibo.com/MoreWindows


推荐阅读
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • vue使用
    关键词: ... [详细]
  • PHP图片截取方法及应用实例
    本文介绍了使用PHP动态切割JPEG图片的方法,并提供了应用实例,包括截取视频图、提取文章内容中的图片地址、裁切图片等问题。详细介绍了相关的PHP函数和参数的使用,以及图片切割的具体步骤。同时,还提供了一些注意事项和优化建议。通过本文的学习,读者可以掌握PHP图片截取的技巧,实现自己的需求。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • 浏览器中的异常检测算法及其在深度学习中的应用
    本文介绍了在浏览器中进行异常检测的算法,包括统计学方法和机器学习方法,并探讨了异常检测在深度学习中的应用。异常检测在金融领域的信用卡欺诈、企业安全领域的非法入侵、IT运维中的设备维护时间点预测等方面具有广泛的应用。通过使用TensorFlow.js进行异常检测,可以实现对单变量和多变量异常的检测。统计学方法通过估计数据的分布概率来计算数据点的异常概率,而机器学习方法则通过训练数据来建立异常检测模型。 ... [详细]
  • 本文介绍了如何使用Express App提供静态文件,同时提到了一些不需要使用的文件,如package.json和/.ssh/known_hosts,并解释了为什么app.get('*')无法捕获所有请求以及为什么app.use(express.static(__dirname))可能会提供不需要的文件。 ... [详细]
  • 本文讨论了一个数列求和问题,该数列按照一定规律生成。通过观察数列的规律,我们可以得出求解该问题的算法。具体算法为计算前n项i*f[i]的和,其中f[i]表示数列中有i个数字。根据参考的思路,我们可以将算法的时间复杂度控制在O(n),即计算到5e5即可满足1e9的要求。 ... [详细]
author-avatar
手机用户2702932960
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有