進程同步演示
一、實驗目的
• 深入掌握進程同步機制——信號量機制的應用;
• 掌握Windows編程中信號量機制的使用方法;
• 可進行簡單的信號量應用編程。
二、實驗工具
Windows系統 + VisuaStudio2019
三、實驗內容
1、複習教材上信號量機制的定義與應用,複習經典進程同步問題——生產者消費者問題及其同步方案;
2、驗證後附的參考代碼pc.cpp(生產者消費者問題),掌握Windows系統中信號量的定義與使用方法;
注意:
1)代碼中 生產者 和 消費者 所做的工作 用過程Producer和Consumer描述,並通過創建線程的方法創建3個生產者線程和1個消費者線程,具體創建方法:CreateThread(NULL,0,Producer,NULL,0,&producerID[i]);其中第3個參數就是指定該線程所做的工作爲過程Producer;
2)問題中設置了三個信號量g_hMutex(用於互斥訪問臨界區buffer)、g_hFullSemaphore、g_hEmptySemaphore(用於控制同步的資源信號量),先聲明,再定義,最後使用。互斥信號量和資源信號量的定義方法不同:
g_hMutex = CreateMutex(NULL,FALSE,NULL); 互斥信號量最開始沒有指定針對那個資源
g_hFullSemaphore = CreateSemaphore(NULL,SIZE_OF_BUFFER-1,SIZE_OF_BUFFER-1,NULL); 其中第2和3個參數爲信號量的初始值和最大值
信號量的使用方法:WaitForSingleObject爲信號量的P操作,每對一個信號量執行該操作,則信號量值減1,並判斷減1後值是否仍大於等於0,如是則該操作成功,否則進程阻塞; ReleaseSemaphore爲信號量的V操作,每執行一次將該信號量的值加1,並起到喚醒作用。如:
WaitForSingleObject(g_hFullSemaphore,INFINITE);
…
ReleaseSemaphore(g_hEmptySemaphore,1,NULL);
ReleaseMutex(g_hMutex);
3、撰寫實驗報告,請在實驗報告中給出運行結果,並對程序功能進行分析,可截圖
代碼段
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <iostream>
const unsigned short SIZE_OF_BUFFER = 10; //緩衝區長度
unsigned short ProductID = 0; //產品號
unsigned short ConsumeID = 0; //將被消耗的產品號
unsigned short in = 0; //產品進緩衝區時的緩衝區下標
unsigned short out = 0; //產品出緩衝區時的緩衝區下標
int g_buffer[SIZE_OF_BUFFER]; //緩衝區是個循環隊列
bool g_continue = true; //控制程序結束
HANDLE g_hMutex; //用於線程間的互斥
HANDLE g_hFullSemaphore; //當緩衝區滿時迫使生產者等待
HANDLE g_hEmptySemaphore; //當緩衝區空時迫使消費者等待
DWORD WINAPI Producer(LPVOID); //生產者線程
DWORD WINAPI Consumer(LPVOID); //消費者線程
int main()
{
//創建各個互斥信號
g_hMutex = CreateMutex(NULL, FALSE, NULL);
g_hFullSemaphore = CreateSemaphore(NULL, SIZE_OF_BUFFER - 1, SIZE_OF_BUFFER - 1, NULL);
g_hEmptySemaphore = CreateSemaphore(NULL, 0, SIZE_OF_BUFFER - 1, NULL);
//調整下面的數值,可以發現,當生產者個數多於消費者個數時,
//生產速度快,生產者經常等待消費者;反之,消費者經常等待
const unsigned short PRODUCERS_COUNT = 3; //生產者的個數
const unsigned short CONSUMERS_COUNT = 1; //消費者的個數
//總的線程數
const unsigned short THREADS_COUNT = PRODUCERS_COUNT + CONSUMERS_COUNT;
HANDLE hThreads[THREADS_COUNT]; //各線程的handle
DWORD producerID[PRODUCERS_COUNT]; //生產者線程的標識符
DWORD consumerID[CONSUMERS_COUNT]; //消費者線程的標識符
//創建生產者線程
for (int i = 0; i < PRODUCERS_COUNT; ++i) {
hThreads[i] = CreateThread(NULL, 0, Producer, NULL, 0, &producerID[i]);
if (hThreads[i] == NULL) return -1;
}
//創建消費者線程
for (int i = 0; i < CONSUMERS_COUNT; ++i) {
hThreads[PRODUCERS_COUNT + i] = CreateThread(NULL, 0, Consumer, NULL, 0, &consumerID[i]);
if (hThreads[i] == NULL) return -1;
}
while (g_continue) {
if (getchar()) { //按回車後終止程序運行
g_continue = false;
}
}
return 0;
}
//生產一個產品。簡單模擬了一下,僅輸出新產品的ID號
void Produce()
{
std::cerr << "Producing " << ++ProductID << " ... ";
std::cerr << "Succeed" << std::endl;
}
//把新生產的產品放入緩衝區
void Append()
{
std::cerr << "Appending a product ... ";
g_buffer[in] = ProductID;
in = (in + 1) % SIZE_OF_BUFFER;
std::cerr << "Succeed" << std::endl;
//輸出緩衝區當前的狀態
for (int i = 0; i < SIZE_OF_BUFFER; ++i) {
std::cout << i << ": " << g_buffer[i];
if (i == in) std::cout << " <-- 生產";
if (i == out) std::cout << " <-- 消費";
std::cout << std::endl;
}
}
//從緩衝區中取出一個產品
void Take()
{
std::cerr << "Taking a product ... ";
ConsumeID = g_buffer[out];
out = (out + 1) % SIZE_OF_BUFFER;
std::cerr << "Succeed" << std::endl;
//輸出緩衝區當前的狀態
for (int i = 0; i < SIZE_OF_BUFFER; ++i) {
std::cout << i << ": " << g_buffer[i];
if (i == in) std::cout << " <-- 生產";
if (i == out) std::cout << " <-- 消費";
std::cout << std::endl;
}
}
//消耗一個產品
void Consume()
{
std::cerr << "Consuming " << ConsumeID << " ... ";
std::cerr << "Succeed" << std::endl;
}
//生產者
DWORD WINAPI Producer(LPVOID lpPara)
{
while (g_continue) {
WaitForSingleObject(g_hFullSemaphore, INFINITE);
WaitForSingleObject(g_hMutex, INFINITE);
Produce();
Append();
Sleep(1500);
ReleaseMutex(g_hMutex);
ReleaseSemaphore(g_hEmptySemaphore, 1, NULL);
}
return 0;
}
//消費者
DWORD WINAPI Consumer(LPVOID lpPara)
{
while (g_continue) {
WaitForSingleObject(g_hEmptySemaphore, INFINITE);
WaitForSingleObject(g_hMutex, INFINITE);
Take();
Consume();
Sleep(1500);
ReleaseMutex(g_hMutex);
ReleaseSemaphore(g_hFullSemaphore, 1, NULL);
}
return 0;
}