實驗內容
在 windows 環境下,利用高級語言編程環境(限定爲 VS 環境或 VC 環境) 調用 CreateThread 函數和相關的同步函數,模擬實現“生產者-消費者”問題。
實驗過程
首先,先寫個生成隨機數的函數,代碼如下:
int random(void){
int a=time (NULL);
srand(a);
return rand()%1000;
}
然後,是生產者的功能函數,主要代碼如下:
if(id == 1) number = random()+1000;
else number = random()+2000;
Buffer[rear]=number;
printf("生產者%d生產Buffer[%d],%d\n",id,rear,Buffer[rear]);
rear = (rear+1)%N;
Sleep(600);
之後,需要加入pv操作以及臨界區。這也是本題的難點所在。下面介紹windows環境是如何實現這些操作的。
臨界區
Windows有專門的臨界區對象。其應用也非常簡單。如下:
CRITICAL_SECTION cs; //聲明臨界區對象
InitializeCriticalSection(&cs); //初始化臨界區對象
EnterCriticalSection(&cs); //進入臨界區
LeaveCriticalSection(&cs); //離開臨界區
信號量機制
Windows通過信號量機制來實現p操作和v操作。其應用函數如下:
HANDLE full =NULL; //聲明
full = CreateSemaphore(NULL ,0 ,N,"full"); //創建
CloseHandle(full); //關閉
HANDLE full =OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"full"); //打開
WaitForSingleObject(full,INFINITE); //p操作
ReleaseSemaphore(empty,1,NULL); //v操作
在這些函數中
CreateSemaphore(NULL ,0 ,N,“full”)函數的第二個參數是信號量的初始值,應大於等於0;第三個參數爲信號量的最大值,第四個參數爲信號量的名稱(可用於在其他地方打開該信號量)。
ReleaseSemaphore(empty,1,NULL)函數的第二個參數代表釋放之後信號量的值加n。
瞭解了windows的臨界區對象和信號量機制之後,我們就可以理解生產者功能函數加入這兩者之後的代碼了:
DWORD WINAPI Producer(LPVOID lpParam){
int number;
int id = ++i;
HANDLE full =OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"full");
HANDLE empty =OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"empty");
while(1){
if(id == 1) number = random()+1000;
else number = random()+2000;
//p(empty)
WaitForSingleObject(empty,INFINITE);
EnterCriticalSection(&cs);
Buffer[rear]=number;
printf("生產者%d生產Buffer[%d],%d\n",id,rear,Buffer[rear]);
flag++;
if(!flag){
fp=fopen("C:\\Users\\HP\\Desktop\\記錄.txt","w");
if(fp==NULL){
printf("can not load file!");
exit(0);
}
}
fprintf(fp,"生產者%d生產Buffer[%d],%d\n",id,rear,Buffer[rear]);
rear = (rear+1)%N;
LeaveCriticalSection(&cs);
//v(full)
ReleaseSemaphore(full,1,NULL);
Sleep(600);
}
}
以同樣的方式,我們可以寫出消費者的功能函數,然後再在主函數中用CreateThread調用這兩個子程序,生成多線程。
完整代碼:
#include <stdio.h>
#include <windows.h>
#include <time.h>
#define N 10
FILE *fp=NULL;
int flag = -1;
int Buffer[N];
int front=0,rear=0,i=0,j=0;
CRITICAL_SECTION cs;
int random(void){
int a=time (NULL);
srand(a);
return rand()%1000;
}
//子線程函數
DWORD WINAPI Producer(LPVOID lpParam){
int number;
int id = ++i;
HANDLE full =OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"full");
HANDLE empty =OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"empty");
while(1){
if(id == 1) number = random()+1000;
else number = random()+2000;
//p(empty)
WaitForSingleObject(empty,INFINITE);
EnterCriticalSection(&cs);
Buffer[rear]=number;
printf("生產者%d生產Buffer[%d],%d\n",id,rear,Buffer[rear]);
flag++;
if(!flag){
fp=fopen("C:\\Users\\HP\\Desktop\\記錄.txt","w");
if(fp==NULL){
printf("can not load file!");
exit(0);
}
}
fprintf(fp,"生產者%d生產Buffer[%d],%d\n",id,rear,Buffer[rear]);
rear = (rear+1)%N;
LeaveCriticalSection(&cs);
//v(full)
ReleaseSemaphore(full,1,NULL);
Sleep(600);
}
}
//子線程函數
DWORD WINAPI Consumer(LPVOID lpParam){
int id = ++j;
HANDLE full =OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"full");
HANDLE empty =OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"empty");
while(1){
//p(full)
WaitForSingleObject(full,INFINITE);
EnterCriticalSection(&cs);
printf("\t消費者%d消費Buffer[%d],%d\n",id,front,Buffer[front]);
flag++;
if(!flag){
fp=fopen("C:\\Users\\HP\\Desktop\\記錄.txt","w");
if(fp==NULL){
printf("can not load file!");
exit(0);
}
}
fprintf(fp,"\t消費者%d消費Buffer[%d],%d\n",id,front,Buffer[front]);
front = (front+1)%N;
LeaveCriticalSection(&cs);
//v(empty)
ReleaseSemaphore(empty,1,NULL);
Sleep(1000);
}
}
//主函數
int main(){
InitializeCriticalSection(&cs); //初始化臨界區對象
HANDLE full =NULL;
full = CreateSemaphore(NULL ,0 ,N,"full");
HANDLE empty =NULL;
empty = CreateSemaphore(NULL ,N,N,"empty");
HANDLE hThread[5];
hThread[0] = CreateThread(NULL,0,Producer,NULL,0,NULL);
hThread[1] = CreateThread(NULL,0,Producer,NULL,0,NULL);
hThread[2] = CreateThread(NULL,0,Consumer,NULL,0,NULL);
hThread[3] = CreateThread(NULL,0,Consumer,NULL,0,NULL);
hThread[4] = CreateThread(NULL,0,Consumer,NULL,0,NULL);
WaitForMultipleObjects(5,hThread,TRUE,10000); //等待子線程運行
fclose(fp);
CloseHandle(full);
CloseHandle(empty);
return 0;
}