多線程面試題系列(16):多線程十大經典案例之一 雙線程讀寫隊列數據
前十五篇中介紹多線程的相關概念,多線程同步互斥問題(第四篇)及解決多線程同步互斥的常用方法——關鍵段、事件、互斥量、信號量、讀寫鎖。為了讓大家更加熟練運用多線程,將會有十篇文章來講解十個多線程使用案例,相信看完這十篇後會讓你能更加遊刃有余的使用多線程。
首先來看第一篇——第十六篇 多線程十大經典案例之一 雙線程讀寫隊列數據
《多線程十大經典案例之一雙線程讀寫隊列數據》案例描述:
MFC對話框中一個按鈕的響應函數實現兩個功能:
顯示數據同時處理數據,因此開兩個線程,一個線程顯示數據(開了一個定時器,響應WM_TIMER消息按照一定時間間隔向TeeChart圖表添加數據並顯示)同時在隊列隊尾添加數據,另一個線程從該隊列隊頭去數據來處理。
下面就來解決這個案例。先來分析下。
《多線程十大經典案例之一雙線程讀寫隊列數據》案例分析:
這個案例是一個線程向隊列中的隊列頭部讀取數據,一個線程向隊列中的隊列尾部寫入數據。看起來很像讀者寫者問題(見十一篇和十四篇),但其實不然,如果將隊列看成緩沖區,這個案例明顯是個生產者消費者問題(見第十篇)。因此我們仿照生產者消費者的思路來具體分析下案例中的“等待”情況:
1. 當隊列為空時,讀取數據線程必須等待寫入數據向隊列中寫入數據。也就是說當隊列為空時,讀取數據線程要等待隊列中有數據。
2. 當隊列滿時,寫入數據線程必須等待讀取數據線程向隊列中讀取數據。也就是說當隊列滿時,寫入數據線程要等待隊列中有空位
在訪問隊列時,需要互斥嗎?這將依賴於隊列的數據結構實現,如果使用STL中的vector,由於vector會動態增長。因此要做互斥保護。如果使用循環隊列,那麽讀取數據線程擁有讀取指針,寫入數據線程擁有寫入指針,各自將訪問隊列中不同位置上的數據,因此不用進行互斥保護。
分析完畢後,再來考慮使用什麽樣的數據結構,同樣依照第十篇中的做法。使用兩個信號量,一個來記錄循環隊列中空位的個數,一個來記錄循環隊列中產品的個數(非空位個數)。代碼非常容易寫出,下面給出完整的源代碼。
代碼中的信號量相關函數可以參考第八篇,代碼中的SetConsoleColor是用來改變控制臺的文字顏色,具體可以參考《VC 控制臺顏色設置》。
《多線程十大經典案例之一雙線程讀寫隊列數據》完整代碼:
[cpp] view plain copy- //第十六篇 多線程十大經典案例之一 雙線程讀寫隊列數據
- //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;
- }
《多線程十大經典案例之一雙線程讀寫隊列數據》運行結果:
程序運行結果如下:
本文配套程序下載地址為:http://download.csdn.net/detail/morewindows/5136035
多線程面試題系列(16):多線程十大經典案例之一 雙線程讀寫隊列數據