生活随笔
收集整理的這篇文章主要介紹了
秒杀多线程第十篇 生产者消费者问题
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
繼經典線程同步問題之后,我們來看看生產者消費者問題及讀者寫者問題。生產者消費者問題是一個著名的線程同步問題,該問題描述如下:有一個生產者在生產產品,這些產品將提供給若干個消費者去消費,為了使生產者和消費者能并發執行,在兩者之間設置一個具有多個緩沖區的緩沖池,生產者將它生產的產品放入一個緩沖區中,消費者可以從緩沖區中取走產品進行消費,顯然生產者和消費者之間必須保持同步,即不允許消費者到一個空的緩沖區中取產品,也不允許生產者向一個已經放入產品的緩沖區中再次投放產品。
??? 這個生產者消費者題目不僅常用于操作系統的課程設計,也常常在程序員和軟件設計師考試中出現。并且在計算機考研的專業課考試中也是一個非常熱門的問題。因此現在就針對這個問題進行詳細深入的解答。
?
??? 首先來簡化問題,先假設生產者和消費者都只有一個,且緩沖區也只有一個。這樣情況就簡便多了。
??? 第一.從緩沖區取出產品和向緩沖區投放產品必須是互斥進行的。可以用關鍵段和互斥量來完成。
??? 第二.生產者要等待緩沖區為空,這樣才可以投放產品,消費者要等待緩沖區不為空,這樣才可以取出產品進行消費。并且由于有二個等待過程,所以要用二個事件或信號量來控制。
??? 考慮這二點后,代碼很容易寫出來。另外為了美觀起見,將消費者的輸出顏色設置為彩色,有關如何在控制臺下設置彩色輸出請參閱《VC?控制臺顏色設置》。
[cpp]?view plaincopy
?? ?? ?? #include?<stdio.h>?? #include?<process.h>?? #include?<windows.h>?? ?? BOOL?SetConsoleColor(WORD?wAttributes)?? {?? ????HANDLE?hConsole?=?GetStdHandle(STD_OUTPUT_HANDLE);?? ????if?(hConsole?==?INVALID_HANDLE_VALUE)?? ????????return?FALSE;????? ????return?SetConsoleTextAttribute(hConsole,?wAttributes);?? }?? const?int?END_PRODUCE_NUMBER?=?10;????? int?g_Buffer;?????????????????????????? ?? CRITICAL_SECTION?g_cs;?? HANDLE?g_hEventBufferEmpty,?g_hEventBufferFull;?? ?? unsigned?int?__stdcall?ProducerThreadFun(PVOID?pM)?? {?? ????for?(int?i?=?1;?i?<=?END_PRODUCE_NUMBER;?i++)?? ????{?? ?????????? ????????WaitForSingleObject(g_hEventBufferEmpty,?INFINITE);?? ?? ?????????? ????????EnterCriticalSection(&g_cs);?? ????????g_Buffer?=?i;?? ????????printf("生產者將數據%d放入緩沖區\n",?i);?? ????????LeaveCriticalSection(&g_cs);?? ?????????? ?????????? ????????SetEvent(g_hEventBufferFull);?? ????}?? ????return?0;?? }?? ?? unsigned?int?__stdcall?ConsumerThreadFun(PVOID?pM)?? {?? ????volatile?bool?flag?=?true;?? ????while?(flag)?? ????{?? ?????????? ????????WaitForSingleObject(g_hEventBufferFull,?INFINITE);?? ?????????? ?????????? ????????EnterCriticalSection(&g_cs);?? ????????SetConsoleColor(FOREGROUND_GREEN);?? ????????printf("??消費者從緩沖區中取數據%d\n",?g_Buffer);?? ????????SetConsoleColor(FOREGROUND_RED?|?FOREGROUND_GREEN?|?FOREGROUND_BLUE);?? ????????if?(g_Buffer?==?END_PRODUCE_NUMBER)?? ????????????flag?=?false;?? ????????LeaveCriticalSection(&g_cs);?? ?????????? ?????????? ????????SetEvent(g_hEventBufferEmpty);?? ?? ????????Sleep(10);??? ????}?? ????return?0;?? }?? int?main()?? {?? ????printf("??生產者消費者問題???1生產者?1消費者?1緩沖區\n");?? ????printf("?--?by?MoreWindows(?http://blog.csdn.net/MoreWindows?)?--\n\n");?? ?? ????InitializeCriticalSection(&g_cs);?? ?????? ????g_hEventBufferEmpty?=?CreateEvent(NULL,?FALSE,?TRUE,?NULL);?? ????g_hEventBufferFull?=?CreateEvent(NULL,?FALSE,?FALSE,?NULL);?? ?????? ????const?int?THREADNUM?=?2;?? ????HANDLE?hThread[THREADNUM];?? ?????? ????hThread[0]?=?(HANDLE)_beginthreadex(NULL,?0,?ProducerThreadFun,?NULL,?0,?NULL);?? ????hThread[1]?=?(HANDLE)_beginthreadex(NULL,?0,?ConsumerThreadFun,?NULL,?0,?NULL);?? ????WaitForMultipleObjects(THREADNUM,?hThread,?TRUE,?INFINITE);?? ????CloseHandle(hThread[0]);?? ????CloseHandle(hThread[1]);?? ?????? ?????? ????CloseHandle(g_hEventBufferEmpty);?? ????CloseHandle(g_hEventBufferFull);?? ????DeleteCriticalSection(&g_cs);?? ????return?0;?? }??
運行結果如下所示:
可以看出生產者與消費者已經是有序的工作了。
?
??? 然后再對這個簡單生產者消費者問題加大難度。將消費者改成2個,緩沖池改成擁有4個緩沖區的大緩沖池。
????如何來思考了這個問題了?首先根據上面分析的二點,可以知道生產者和消費者由一個變成多個的影響不大,唯一要注意的是緩沖池變大了,回顧一下《秒殺多線程第八篇?經典線程同步 信號量Semaphore》中的信號量,不難得出用二個信號量就可以解決這種緩沖池有多個緩沖區的情況——用一個信號量A來記錄為空的緩沖區個數,另一個信號量B記錄非空的緩沖區個數,然后生產者等待信號量A,消費者等待信號量B就可以了。因此可以仿照上面的代碼來實現復雜生產者消費者問題,示例代碼如下:
[cpp]?view plaincopy
?? #include?<stdio.h>?? #include?<process.h>?? #include?<windows.h>?? ?? BOOL?SetConsoleColor(WORD?wAttributes)?? {?? ????HANDLE?hConsole?=?GetStdHandle(STD_OUTPUT_HANDLE);?? ????if?(hConsole?==?INVALID_HANDLE_VALUE)?? ????????return?FALSE;?? ?????? ????return?SetConsoleTextAttribute(hConsole,?wAttributes);?? }?? const?int?END_PRODUCE_NUMBER?=?8;???? const?int?BUFFER_SIZE?=?4;???????????? int?g_Buffer[BUFFER_SIZE];???????????? int?g_i,?g_j;?? ?? CRITICAL_SECTION?g_cs;?? HANDLE?g_hSemaphoreBufferEmpty,?g_hSemaphoreBufferFull;?? ?? unsigned?int?__stdcall?ProducerThreadFun(PVOID?pM)?? {?? ????for?(int?i?=?1;?i?<=?END_PRODUCE_NUMBER;?i++)?? ????{?? ?????????? ????????WaitForSingleObject(g_hSemaphoreBufferEmpty,?INFINITE);?? ?? ?????????? ????????EnterCriticalSection(&g_cs);?? ????????g_Buffer[g_i]?=?i;?? ????????printf("生產者在緩沖池第%d個緩沖區中投放數據%d\n",?g_i,?g_Buffer[g_i]);?? ????????g_i?=?(g_i?+?1)?%?BUFFER_SIZE;?? ????????LeaveCriticalSection(&g_cs);?? ?? ?????????? ????????ReleaseSemaphore(g_hSemaphoreBufferFull,?1,?NULL);?? ????}?? ????printf("生產者完成任務,線程結束運行\n");?? ????return?0;?? }?? ?? unsigned?int?__stdcall?ConsumerThreadFun(PVOID?pM)?? {?? ????while?(true)?? ????{?? ?????????? ????????WaitForSingleObject(g_hSemaphoreBufferFull,?INFINITE);?? ?????????? ?????????? ????????EnterCriticalSection(&g_cs);?? ????????SetConsoleColor(FOREGROUND_GREEN);?? ????????printf("??編號為%d的消費者從緩沖池中第%d個緩沖區取出數據%d\n",?GetCurrentThreadId(),?g_j,?g_Buffer[g_j]);?? ????????SetConsoleColor(FOREGROUND_RED?|?FOREGROUND_GREEN?|?FOREGROUND_BLUE);?? ????????if?(g_Buffer[g_j]?==?END_PRODUCE_NUMBER)?? ????????{?? ????????????LeaveCriticalSection(&g_cs);?? ?????????????? ????????????ReleaseSemaphore(g_hSemaphoreBufferFull,?1,?NULL);?? ????????????break;?? ????????}?? ????????g_j?=?(g_j?+?1)?%?BUFFER_SIZE;?? ????????LeaveCriticalSection(&g_cs);?? ?? ????????Sleep(50);??? ?? ????????ReleaseSemaphore(g_hSemaphoreBufferEmpty,?1,?NULL);?? ????}?? ????SetConsoleColor(FOREGROUND_GREEN);?? ????printf("??編號為%d的消費者收到通知,線程結束運行\n",?GetCurrentThreadId());?? ????SetConsoleColor(FOREGROUND_RED?|?FOREGROUND_GREEN?|?FOREGROUND_BLUE);?? ????return?0;?? }?? int?main()?? {?? ????printf("??生產者消費者問題???1生產者?2消費者?4緩沖區\n");?? ????printf("?--?by?MoreWindows(?http://blog.csdn.net/MoreWindows?)?--\n\n");?? ?? ????InitializeCriticalSection(&g_cs);?? ?????? ????g_hSemaphoreBufferEmpty?=?CreateSemaphore(NULL,?4,?4,?NULL);?? ????g_hSemaphoreBufferFull??=?CreateSemaphore(NULL,?0,?4,?NULL);?? ????g_i?=?0;?? ????g_j?=?0;?? ????memset(g_Buffer,?0,?sizeof(g_Buffer));?? ?? ????const?int?THREADNUM?=?3;?? ????HANDLE?hThread[THREADNUM];?? ?????? ????hThread[0]?=?(HANDLE)_beginthreadex(NULL,?0,?ProducerThreadFun,?NULL,?0,?NULL);?? ?????? ????hThread[1]?=?(HANDLE)_beginthreadex(NULL,?0,?ConsumerThreadFun,?NULL,?0,?NULL);?? ????hThread[2]?=?(HANDLE)_beginthreadex(NULL,?0,?ConsumerThreadFun,?NULL,?0,?NULL);?? ????WaitForMultipleObjects(THREADNUM,?hThread,?TRUE,?INFINITE);?? ????for?(int?i?=?0;?i?<?THREADNUM;?i++)?? ????????CloseHandle(hThread[i]);?? ?? ?????? ????CloseHandle(g_hSemaphoreBufferEmpty);?? ????CloseHandle(g_hSemaphoreBufferFull);?? ????DeleteCriticalSection(&g_cs);?? ????return?0;?? }??
運行結果如下圖所示:
輸出結果證明各線程的同步和互斥已經完成了。
?
至此,生產者消費者問題已經圓滿的解決了,下面作個總結:
1.首先要考慮生產者與消費者對緩沖區操作時的互斥。
2.不管生產者與消費者有多少個,緩沖池有多少個緩沖區。都只有二個同步過程——分別是生產者要等待有空緩沖區才能投放產品,消費者要等待有非空緩沖區才能去取產品。
?
下一篇《秒殺多線程第十一篇讀者寫者問題》將介紹另一個著名的同步問題——讀者寫者問題,歡迎大家再來參閱。
?
轉載請標明出處,原文地址:http://blog.csdn.net/morewindows/article/details/7577591
如果覺得本文對您有幫助,請點擊‘頂’支持一下,您的支持是我寫作最大的動力,謝謝。
?
總結
以上是生活随笔為你收集整理的秒杀多线程第十篇 生产者消费者问题的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。