zoukankan      html  css  js  c++  java
  • 多线程面试题系列(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 ", 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写入队列 ", 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("     第十六篇 多线程十大经典案例 双线程读写队列数据 ");  
    61.     printf(" - by MoreWindows( http://blog.csdn.net/MoreWindows/article/details/8646902 ) - ");  
    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

  • 相关阅读:
    分享:CodeLite 5.1 发布,C++ 集成开发环境
    Elementary Sockets:readn, writen, and readline Functions
    How to handle boost::iostreams::gzip_dec C++ Forum
    分享:python/c++ 深拷贝与浅拷贝(转)写∽好
    我刚刚通过 @盛大网盘EverBox 分享了 Web协议与实践HTTP.、网络协议、缓存技术和....pdf, 欢迎大家下载!
    Is zlib threadsafe?
    print "\v"
    分享:混合编译c和cpp的makefile模板
    Elementary Sockets:readn, writen, and readline Functions
    c的socket教程 二进制 博客频道 CSDN.NET
  • 原文地址:https://www.cnblogs.com/dengyungao/p/7504125.html
Copyright © 2011-2022 走看看