設計并實現(xiàn)一個進程,該進程擁有一個生產(chǎn)者線程和一個消費者線程,它們使用N個不同的緩沖區(qū)(N為一個確定的數(shù)值,例如N=32)犀勒。需要使用如下信號量:
一個互斥信號量,用以阻止生產(chǎn)者線程和消費者線程同時操作緩沖區(qū)列表妥曲;
一個信號量账蓉,當生產(chǎn)者線程生產(chǎn)出一個物品時可以用它向消費者線程發(fā)出信號;
一個信號量逾一,消費者線程釋放出一個空緩沖區(qū)時可以用它向生產(chǎn)者線程發(fā)出信號铸本。
主要程序結構
#include<iostream>
#include<Windows.h>
#include<process.h>
#include<vector>
using namespace std;
// 等價于WINAPI,約定使用stdcall函數(shù)調用遵堵,既被調用者負責清棧
#define STD __stdcall
// 確定緩沖區(qū)大小箱玷,這里選取5怨规,便于展示和說明結果
#define LENGTH 5
// 隨機時間
#define GETMYRAND() (int)(((double)rand()/(double)RAND_MAX)*300)
// 使用臨界區(qū)來同步線程
CRITICAL_SECTION _cr;
//空信號量
HANDLE emptySemaphore = NULL;
//滿信號量
HANDLE fullSemaphore = NULL;
// 緩沖區(qū)Buffer
vector<int> buffer;
// 消費者線程
DWORD STD Consumer(void* lp) {
while(true) {
//等待判斷緩沖區(qū)滿的信號量
WaitForSingleObject(fullSemaphore,0xFFFFFFFF);
//進入臨界區(qū),線程同步锡足,功能同互斥量
EnterCriticalSection(&_cr);
//消費者線程從緩沖區(qū)中取出消費一個資源
buffer.pop_back();
//打印當前緩沖區(qū)可用資源數(shù)
cout << "消費者消費一個資源波丰,當前可用資源數(shù):" << buffer.size() << endl;
//離開臨界區(qū)
LeaveCriticalSection(&_cr);
//釋放判斷緩沖區(qū)空的信號量
ReleaseSemaphore(emptySemaphore,1,NULL);
//線程睡眠隨機時間
Sleep(GETMYRAND());
}
return 0;
}
// 生產(chǎn)者線程
DWORD STD Producer(void* lp) {
while(true){
//等待判斷緩沖區(qū)空的信號量
WaitForSingleObject(emptySemaphore, 0xFFFFFFFF);
//進入臨界區(qū),線程同步舶得,功能同互斥量
EnterCriticalSection(&_cr);
//生產(chǎn)者線程向緩沖區(qū)中生成一個資源
buffer.push_back(1);
//打印當前緩沖區(qū)可用資源數(shù)
cout << "生產(chǎn)者新生產(chǎn)一個資源掰烟,當前可用資源數(shù):" << buffer.size() << endl;
//離開臨界區(qū)
LeaveCriticalSection(&_cr);
//釋放判斷緩沖區(qū)滿的信號量
ReleaseSemaphore(fullSemaphore, 1, NULL);
//線程睡眠隨機時間
Sleep(GETMYRAND());
}
return 0;
}
int main() {
//創(chuàng)建信號量
emptySemaphore = CreateSemaphore(NULL, LENGTH, LENGTH, NULL);
fullSemaphore = CreateSemaphore(NULL, 0, LENGTH, NULL);
//初始化臨界區(qū)
InitializeCriticalSection(&_cr);
//開啟多線程
HANDLE handles[2];
handles[1] = CreateThread(0, 0, &Producer, 0, 0, 0);
handles[0] = CreateThread(0, 0, &Consumer, 0, 0, 0);
//等待子線程執(zhí)行完畢
WaitForMultipleObjects(2, handles, true, INFINITE); //"Join" trreads
//釋放子線程
CloseHandle(handles[0]);
CloseHandle(handles[1]);
//釋放臨界區(qū)
DeleteCriticalSection(&_cr);
return 0;
}
一、生產(chǎn)者消費者線程互斥
使用critical area(臨界區(qū))來保證生產(chǎn)者和消費者的線程彼此互斥:
在多線程開啟前后分別初始化臨界區(qū)和銷毀臨界區(qū)沐批。
接下來纫骑,在生產(chǎn)者線程函數(shù)和消費者線程函數(shù)對緩沖區(qū)進行操作的代碼前后啟用臨界區(qū)和離開臨界區(qū),來做到兩個線程間互斥同步九孩。
最終效果是先馆,雖然兩個線程都通過死循環(huán)不斷執(zhí)行循環(huán)體中代碼,但是在其中一個線程執(zhí)行臨界區(qū)代碼時躺彬,另一個線程被互斥阻塞煤墙。
二、生產(chǎn)者宪拥、消費者分別在“滿”和“空”時阻塞
生產(chǎn)者線程得到emptySemaphore信號量時,其計數(shù)器-1仿野,開始生產(chǎn)資源。在生產(chǎn)結束后她君,使fullSemaphore的計數(shù)器+1设预。
消費者線程得到fullSemaphore信號量時,其計數(shù)器-1,開始生產(chǎn)資源犁河。在生產(chǎn)結束后,使emptySemaphore的計數(shù)器+1魄梯。
通過上面所述過程桨螺,使用emptySemaphore和fullSemaphore兩個信號量可以做到資源生產(chǎn)滿時生產(chǎn)者阻塞,資源消費完時消費者阻塞的功能酿秸。
為了演示消費者阻塞效果灭翔,我們把生產(chǎn)者的生產(chǎn)效率降低,既在生產(chǎn)者線程結束后辣苏,Sleep睡眠的時間延長肝箱。
查看效果:
每隔大半秒出現(xiàn)一對生產(chǎn)者和消費者記錄回怜,說明資源不足時矢腻,已經(jīng)阻塞消費者繼續(xù)消費資源,而等待生產(chǎn)者Sleep休眠時間之后娃豹,生產(chǎn)出新的資源退客,消費者才繼續(xù)消費骏融。
為了演示生產(chǎn)者阻塞效果链嘀,我們把消費者的消費效率降低,既在消費者線程結束后档玻,Sleep睡眠的時間延長怀泊。
查看效果:
當資源數(shù)到達5時,資源已滿時误趴,既阻塞生產(chǎn)者繼續(xù)生產(chǎn)資源霹琼,而等待消費者Sleep休眠時間之后,消費掉資源凉当,生產(chǎn)者才繼續(xù)生產(chǎn)枣申。
查閱資料及筆記:
在生產(chǎn)者和消費者的代碼中經(jīng)常出現(xiàn)DWORD WINAPI
其中,WINAPI是宏定義 #define WINAPI __stdcall
stdcall是一種函數(shù)調用約定纤怒,其清棧工作由被調用者執(zhí)行糯而,對于節(jié)省內存效果很好,但缺點是參數(shù)不是可變長類型泊窘。
另外還有__cdcel熄驼、__fastcall等。
臨界區(qū)烘豹、互斥量瓜贾、信號量、事件總結:
1. 互斥量與臨界區(qū)的作用非常相似携悯,但互斥量是可以命名的祭芦,也就是說它可以跨越進程使用。所以創(chuàng)建互斥量需要的資源更多憔鬼,所以如果只為了在進程內部是用的話使用臨界區(qū)會帶來速度上的優(yōu)勢并能夠減少資源占用量 龟劲。因為互斥量是跨進程的互斥量一旦被創(chuàng)建,就可以通過名字打開它轴或。
臨界區(qū)的缺點是:沒有辦法知道進入臨界區(qū)中的那個線程是生是死昌跌。如果那個線程在進入臨界區(qū)后當?shù)袅耍覜]有退出來照雁,那么系統(tǒng)就沒有辦法消除掉此臨界區(qū)蚕愤。
2. 互斥量(Mutex),信號量(Semaphore)饺蚊,事件(Event)都可以被跨越進程使用來進行同步數(shù)據(jù)操作萍诱,而其他的對象與數(shù)據(jù)同步操作無關,但對于進程和線程來講污呼,如果進程和線程在運行狀態(tài)則為無信號狀態(tài)裕坊,在退出后為有信號狀態(tài)。所以可以使用WaitForSingleObject來等待進程和 線程退出燕酷。
3. 通過互斥量可以指定資源被獨占的方式使用碍庵,但如果有下面一種情況通過互斥量就無法處理映企,比如現(xiàn)在一位用戶購買了一份三個并發(fā)訪問許可的數(shù)據(jù)庫系統(tǒng),可以根據(jù)用戶購買的訪問許可數(shù)量來決定有多少個線程/進程能同時進行數(shù)據(jù)庫操作静浴,這時候如果利用互斥量就沒有辦法完成這個要求堰氓,信號燈對象可以說是一種資源計數(shù)器。
mutex和samephore都是信號量苹享,但前者實現(xiàn)critical area的功能双絮,后者帶計數(shù)器。mutex可以說是計數(shù)為1的samephore得问。