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

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

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

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

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

 

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

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

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

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

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

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

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

 

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

  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.       
  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://blog.csdn.net/MoreWindows/article/details/8646902
#include <stdio.h>
#include <process.h>
#include <windows.h>
#include <time.h>
const int QUEUE_LEN = 5;
int g_arrDataQueue[QUEUE_LEN];
int g_i, g_j, g_nDataNum;
//关键段 用于保证互斥的在屏幕上输出
CRITICAL_SECTION g_cs;
//信号量 g_hEmpty表示队列中空位 g_hFull表示队列中非空位
HANDLE     g_hEmpty, g_hFull;
//设置控制台输出颜色
BOOL SetConsoleColor(WORD wAttributes)
{
	HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
	if (hConsole == INVALID_HANDLE_VALUE)
		return FALSE;	
	return SetConsoleTextAttribute(hConsole, wAttributes);
}
//读数据线程函数
unsigned int __stdcall ReaderThreadFun(PVOID pM)
{
	int nData = 0;
	while (nData < 20)
	{
		WaitForSingleObject(g_hFull, INFINITE);
		nData = g_arrDataQueue[g_i];
		g_i = (g_i + 1) % QUEUE_LEN;
		EnterCriticalSection(&g_cs);
		printf("从队列中读数据%d\n", nData);
		LeaveCriticalSection(&g_cs);
		Sleep(rand() % 300);
		ReleaseSemaphore(g_hEmpty, 1, NULL);
	}
	return 0;
}
//写数据线程函数
unsigned int __stdcall WriterThreadFun(PVOID pM)
{
	int nData = 0;
	while (nData < 20)
	{
		WaitForSingleObject(g_hEmpty, INFINITE);
		g_arrDataQueue[g_j] = ++nData;
		g_j = (g_j + 1) % QUEUE_LEN;
		EnterCriticalSection(&g_cs);
		SetConsoleColor(FOREGROUND_GREEN);
		printf("    将数据%d写入队列\n", nData);
		SetConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
		LeaveCriticalSection(&g_cs);
		Sleep(rand() % 300);
		ReleaseSemaphore(g_hFull, 1, NULL);
	}
	return 0;
}
int main()
{
	printf("     秒杀多线程第十六篇 多线程十大经典案例 双线程读写队列数据\n");
	printf(" - by MoreWindows( http://blog.csdn.net/MoreWindows/article/details/8646902 ) -\n\n");
	
	InitializeCriticalSection(&g_cs);
	g_hEmpty = CreateSemaphore(NULL, QUEUE_LEN, QUEUE_LEN, NULL);
	g_hFull = CreateSemaphore(NULL, 0, QUEUE_LEN, NULL);
	
	srand(time(NULL));
	g_i = g_j = 0;
	HANDLE hThread[2];
	hThread[0] = (HANDLE)_beginthreadex(NULL, 0, ReaderThreadFun, NULL, 0, NULL);
	hThread[1] = (HANDLE)_beginthreadex(NULL, 0, WriterThreadFun, NULL, 0, NULL);
	
	WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
	
	for (int i = 0; i < 2; i++)
		CloseHandle(hThread[i]);
	CloseHandle(g_hEmpty);
	CloseHandle(g_hFull);
	DeleteCriticalSection(&g_cs);
	return 0;
}

 

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

程序运行结果如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值