#include<windows.h>
#include<fstream.h>
#include<stdio.h>
#include<string>
#include<conio.h>
//定義一些常量;
//本程式允許的最大臨界區數;
#define MAX_BUFFER_NUM 10
//秒到微秒的乘法因子;
#define INTE_PER_SEC 1000
//本程式允許的生產和消費線程的總數;
#define MAX_THREAD_NUM 64
//定義一個結構,記錄在測試檔案中指定的每一個線程的參數
struct ThreadInfo
{
int serial; //線程式列號
char entity; //是P還是C
double delay; //線程延遲
int thread_request[MAX_THREAD_NUM]; //線程請求隊列
int n_request; //請求個數
};
//全域變數的定義
//臨界區對象的聲明,用於管理緩衝區的互斥訪問;
int Buffer_Critical[MAX_BUFFER_NUM]; //緩衝區聲明,用於存放產品;
ThreadInfo Thread_Info[MAX_THREAD_NUM]; //線程資訊數組;
HANDLE h_Thread[MAX_THREAD_NUM]; //用於儲存每個線程控制代碼的數組;
HANDLE empty_semaphore; //一個訊號量;
HANDLE h_mutex; //一個互斥量;
HANDLE h_Semaphore[MAX_THREAD_NUM]; //生產者允許消費者開始消費的訊號量;
CRITICAL_SECTION PC_Critical[MAX_BUFFER_NUM];
DWORD n_Thread = 0; //實際的線程的數目;
DWORD n_Buffer_or_Critical; //實際的緩衝區或者臨界區的數目;
//生產消費及輔助函數的聲明
void Produce(void *p);
void Consume(void *p);
bool IfInOtherRequest(int);
int FindProducePositon();
int FindBufferPosition(int);
int main(int argc, char **argv)
{
//聲明所需變數;
DWORD wait_for_all;
ifstream inFile;
if (argc!=2) {
printf("Usage:%s <File>/n",argv[0]);
return 1;
}
//初始化緩衝區;
for(int i=0;i< MAX_BUFFER_NUM;i++)
Buffer_Critical[i] = -1;
//初始化每個線程的請求隊列;
for(int j=0;j<MAX_THREAD_NUM;j++) {
for(int k=0;k<MAX_THREAD_NUM;k++)
Thread_Info[j].thread_request[k] = -1;
Thread_Info[j].n_request = 0;
}
//初始化臨界區;
for(i =0;i< MAX_BUFFER_NUM;i++)
InitializeCriticalSection(&PC_Critical[i]);
//開啟輸入檔案,按照規定的格式提取線程等資訊;
inFile.open(argv[1]);
//從檔案中獲得實際的緩衝區的數目,即測試檔案第一行的資訊;
inFile >> n_Buffer_or_Critical;
inFile.get(); // 讀取測試檔案中的空格,將檔案指標指向下一行;
printf("輸入檔案是:/n");
//回顯獲得的緩衝區的數目資訊;
printf("%d /n",(int) n_Buffer_or_Critical);
//提取每個線程的資訊到相應資料結構中;
while(inFile){
inFile >> Thread_Info[n_Thread].serial;
inFile >> Thread_Info[n_Thread].entity;
inFile >> Thread_Info[n_Thread].delay;
char c;
inFile.get(c);
while(c!='/n'&& !inFile.eof()) {
inFile>> Thread_Info[n_Thread].thread_request[Thread_Info[n_Thread].n_request++];
inFile.get(c);
}
n_Thread++;
}
//回顯獲得的線程資訊,便於確認正確性;
for(j=0;j<(int) n_Thread;j++) {
int Temp_serial = Thread_Info[j].serial;
char Temp_entity = Thread_Info[j].entity;
double Temp_delay = Thread_Info[j].delay;
printf(" /nthread%2d %c %f ",Temp_serial,Temp_entity,Temp_delay);
int Temp_request = Thread_Info[j].n_request;
for(int k=0;k<Temp_request;k++)
printf(" %d ", Thread_Info[j].thread_request[k]);
cout<<endl;
}
printf("/n/n");
//建立在類比過程中幾個必要的訊號量
empty_semaphore = CreateSemaphore(NULL,n_Buffer_or_Critical,n_Buffer_or_Critical,
"semaphore_for_empty");
h_mutex = CreateMutex(NULL,FALSE,"mutex_for_update");
//下面這個迴圈用線程的ID號來為相應生產線程的產品讀寫時所
//使用的同步訊號量命名;
for(j=0;j<(int)n_Thread;j++) {
char lp[]="semaphore_for_produce_";
int temp =j;
while(temp){
char c = (char)(temp%10);
strcat(lp,&c);
temp/=10;
}
h_Semaphore[j+1]=CreateSemaphore(NULL,0,n_Thread,lp);
}
//建立生產者和消費者線程;
for(i =0;i< (int) n_Thread;i++) {
if(Thread_Info[i].entity =='P')
h_Thread[i]= CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(Produce),
&(Thread_Info[i]),0,NULL);
else
h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(Consume),
&(Thread_Info[i]),0,NULL);
}
//主程式等待各個線程的動作結束;
wait_for_all = WaitForMultipleObjects(n_Thread,h_Thread,TRUE,-1);
printf(" /n /nALL Producer and consumer have finished their work. /n");
printf("Press any key to quit!/n");
_getch();
return 0;
}
//確認是否還有對同一產品的消費請求未執行;
bool IfInOtherRequest(int req)
{
for(int i=0;i<n_Thread;i++)
for(int j=0;j<Thread_Info[i].n_request;j++)
if(Thread_Info[i].thread_request[j] == req)
return TRUE;
return FALSE;
}
//找出當前可以進行產品生產的空緩衝區位置;
int FindProducePosition()
{
int EmptyPosition;
for (int i =0;i<n_Buffer_or_Critical;i++)
if(Buffer_Critical[i] == -1) {
EmptyPosition = i;
//用下面這個特殊值表示本緩衝區正處於被寫狀態;
Buffer_Critical[i] = -2;
break;
}
return EmptyPosition;
}
//找出當前所需生產者生產的產品的位置;
int FindBufferPosition(int ProPos)
{
int TempPos;
for (int i =0 ;i<n_Buffer_or_Critical;i++)
if(Buffer_Critical[i]==ProPos){
TempPos = i;
break;
}
return TempPos;
}
//生產者進程
void Produce(void *p)
{
//局部變數聲明;
DWORD wait_for_semaphore,wait_for_mutex,m_delay;
int m_serial;
//獲得本線程的資訊;
m_serial = ((ThreadInfo*)(p))->serial;
m_delay = (DWORD)(((ThreadInfo*)(p))->delay *INTE_PER_SEC);
Sleep(m_delay);
//開始請求生產
printf("Producer %2d sends the produce require./n",m_serial);
//互斥訪問下一個可用於生產的空臨界區,實現寫寫互斥;
wait_for_mutex = WaitForSingleObject(h_mutex,-1);
//確認有空緩衝區可供生產,同時將空位置數empty減1;用於生產者和消費者的同步;
//若沒有則一直等待,直到消費者進程釋放資源為止;
wait_for_semaphore = WaitForSingleObject(empty_semaphore,-1);
int ProducePos = FindProducePosition();
ReleaseMutex(h_mutex);
//生產者在獲得自己的空位置並做上標記後,以下的寫操作在生產者之間可以並發;
//核心生產步驟中,程式將生產者的ID作為產品編號放入,方便消費者識別;
printf("Producer %2d begin to produce at position %2d./n",m_serial,ProducePos);
Buffer_Critical[ProducePos] = m_serial;
printf("Producer %2d finish producing :/n ",m_serial);
printf(" position[ %2d ]:%3d /n/n" ,ProducePos,Buffer_Critical[ProducePos]);
//使生產者寫的緩衝區可以被多個消費者使用,實現讀寫同步;
ReleaseSemaphore(h_Semaphore[m_serial],n_Thread,NULL);
}
//消費者進程
void Consume(void * p)
{
//局部變數聲明;
DWORD wait_for_semaphore,m_delay;
int m_serial,m_requestNum; //消費者的序號和請求的數目;
int m_thread_request[MAX_THREAD_NUM]; //本消費線程的請求隊列;
//提取本線程的資訊到本地;
m_serial = ((ThreadInfo*)(p))->serial;
m_delay = (DWORD)(((ThreadInfo*)(p))->delay *INTE_PER_SEC);
m_requestNum = ((ThreadInfo *)(p))->n_request;
for (int i = 0;i<m_requestNum;i++)
m_thread_request[i] = ((ThreadInfo*)(p))->thread_request[i];
Sleep(m_delay);
//迴圈進行所需產品的消費
for(i =0;i<m_requestNum;i++){
//請求消費下一個產品
printf("Consumer %2d request to consume %2d product/n",m_serial,m_thread_request[i]);
//如果對應生產者沒有生產,則等待;如果生產了,允許的消費者數目-1;實現了讀寫同步;
wait_for_semaphore=WaitForSingleObject(h_Semaphore[m_thread_request[i]],-1);
//查詢所需產品放到緩衝區的號
int BufferPos=FindBufferPosition(m_thread_request[i]);
//開始進行具體緩衝區的消費處理,讀和讀在該緩衝區上仍然是互斥的;
//進入臨界區後執行消費動作;並在完成此次請求後,通知另外的消費者本處請求已
//經滿足;同時如果對應的產品使用完畢,就做相應處理;並給出相應動作的介面提
//示;該相應處理指將相應緩衝區清空,並增加代表空緩衝區的訊號量;
EnterCriticalSection(&PC_Critical[BufferPos]);
printf("Consumer %2d begin to consume %2d product /n",m_serial,m_thread_request[i]);
((ThreadInfo*)(p))->thread_request[i] =-1;
if(!IfInOtherRequest(m_thread_request[i])) {
Buffer_Critical[BufferPos] = -1; //-1標記緩衝區為空白;
printf("Consumer %2d finish consuming %2d:/n ",m_serial,m_thread_request[i]);
printf(" position[ %2d ]:%3d /n/n" ,BufferPos,Buffer_Critical[BufferPos]);
ReleaseSemaphore(empty_semaphore,1,NULL);
}
else {
printf("Consumer %2d finish consuming product %2d/n/n ",m_serial,m_thread_request[i]);
}
//離開臨界區
LeaveCriticalSection(&PC_Critical[BufferPos]);
}
}