北京理工大学操作系统 实验二 生产者消费者问题

本文详细描述了一个实验,通过Windows平台实现生产者消费者问题,利用信号量机制进行进程间的同步与互斥,以加深对并发执行和进程通信的理解。实验涉及创建进程、使用信号量、共享内存和字符串操作。
摘要由CSDN通过智能技术生成


Copyright © 2024 Squareroot_2, All rights reserved.

实验二 生产者消费者问题

一、实验目的

通过本实验,加深对进程概念的理解,明确进程与程序的区别;认识并发执行的本质,理解和掌握Windows进程通信系统调用的功能,通过实验掌握使用信号量机制完成进程间同步和互斥的方法。

二、实验内容

• 创建一个有6个缓冲区的缓冲池,初始为空,每个缓冲区能存放一个长度若为10个字符的字符串。
• 2个生产者进程
– 随机等待一段时间,往缓冲区添加数据,
– 若缓冲区已满,等待消费者取走数据后再添加
– 重复12次
• 3个消费者进程
– 随机等待一段时间,从缓冲区读取数据
– 若缓冲区为空,等待生产者添加数据后再读取
– 重复8次
• 在Windows平台上做。
• 显示每次添加或读取数据的时间及缓冲区的映像。
• 生产者和消费者用进程模拟。

三、程序设计与实现

#include <iostream>
#include <windows.h>

#define BUFSIZE 6   //缓冲区大小
#define STRLEN 10   //字符串长度
#define PRO_TYPE 0  //生产者标识
#define PROERNUM 2  //生产者数量
#define PRONUM 12   //每个生产者生产的数据量
#define CON_TYPE 1  //消费者标识
#define CONERNUM 3  //消费者数量
#define CONNUM 8    //每个消费者消费的数据量

static LPCTSTR filemapping_name = "FileMapping";
HANDLE ProcessHandle[5];
using namespace std;

//缓存结构体
typedef struct BUF {
    char buf[BUFSIZE][STRLEN + 1];
    int head;
    int tail;
} buffer;

//返回随机休眠时间
int getRandomSleep() {
    return rand() % 1000 + 100;
}

//返回随机字符
char getRandomChar() {
    return (rand() % 26) + 'A';
}

//定义P操作
DWORD P(HANDLE S) {
    return WaitForSingleObject(S, INFINITE);
}
//定义V操作
BOOL V(HANDLE S) {
    return ReleaseSemaphore(S, 1, nullptr);
}

//创建子进程
void startChildProcess(const int type, const int id) {
    TCHAR filename[MAX_PATH];
    GetModuleFileName(nullptr, filename, MAX_PATH);

    TCHAR cmdLine[MAX_PATH];
    sprintf(cmdLine, "\"%s\" %d", filename, type);

    STARTUPINFO si = {sizeof(STARTUPINFO)};
    PROCESS_INFORMATION pi;

    //新建子进程
    BOOL bCreateOK = CreateProcess(
            filename,
            cmdLine,
            nullptr,
            nullptr,
            FALSE,
            CREATE_DEFAULT_ERROR_MODE,
            nullptr,
            nullptr,
            &si,
            &pi);
    if (bCreateOK) {
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
        ProcessHandle[id] = pi.hProcess;
    } else {
        printf("child process error!\n");
        exit(0);
    }
}

//主进程程序
void useParentProcess() {
    //创建信号量
    HANDLE MUTEX = CreateSemaphore(nullptr,
                                   1,
                                   1,
                                   "mutex");
    HANDLE EMPTY = CreateSemaphore(nullptr,
                                   BUFSIZE,
                                   BUFSIZE,
                                   "empty");
    HANDLE FULL = CreateSemaphore(nullptr,
                                  0,
                                  BUFSIZE,
                                  "full");

    //创建共享内存
    HANDLE hMapping = CreateFileMapping(
            nullptr,
            nullptr,
            PAGE_READWRITE,
            0,
            sizeof(struct BUF),
            filemapping_name);
    if (hMapping != INVALID_HANDLE_VALUE) {
        LPVOID pFile = MapViewOfFile(
                hMapping,
                FILE_MAP_ALL_ACCESS,
                0,
                0,
                0);
        if (pFile != nullptr) {
            ZeroMemory(pFile, sizeof(struct BUF));
        }
        //共享内存的初始化
        buffer *pBuf = reinterpret_cast<buffer *>(pFile);
        memset(pBuf->buf, 0, sizeof(pBuf->buf));
        pBuf->head = 0;
        pBuf->tail = 0;

        //解除映射
        UnmapViewOfFile(pFile);
        pFile = nullptr;
        CloseHandle(pFile);
    }

    //创建2个生产者,3个消费者
    int childProcessNum = PROERNUM + CONERNUM;
    for (int i = 0; i < childProcessNum; ++i) {
        int type = i < PROERNUM ? PRO_TYPE : CON_TYPE;
        startChildProcess(type, i);
    }
    //等待子进程运行
    WaitForMultipleObjects(childProcessNum,
                           ProcessHandle,
                           TRUE,
                           INFINITE);
    //回收信号量
    CloseHandle(EMPTY);
    CloseHandle(FULL);
    CloseHandle(MUTEX);
}

//生产者
void useProducerProcess(HANDLE MUTEX, HANDLE EMPTY, HANDLE FULL, buffer *pbuf) {
    for (int i = 0; i < PRONUM; ++i) {
        P(EMPTY);
        Sleep(getRandomSleep());
        P(MUTEX);
        //生产数据
        char string[STRLEN + 1];
        memset(string, 0, sizeof(string));
        for (int j = 0; j < STRLEN; ++j) {
            string[j] = getRandomChar();
        }
        strcpy(pbuf->buf[pbuf->tail], string);
        pbuf->tail = (pbuf->tail + 1) % BUFSIZE;

        //打印时间
        time_t t = time(nullptr);
        struct tm *ptm = localtime(&t);

        //打印生产数据、时间和缓冲区映像
        printf("\nProducerID:%6d, time: %d:%d:%d, puts data:%-10s\tbuffer 1:%-10s\t2:%-10s\t3:%-10s\t4:%-10s\t5:%-10s\t6:%-10s",
               (int) GetCurrentProcessId(),
               ptm->tm_hour, ptm->tm_min, ptm->tm_sec,
               string, pbuf->buf[0], pbuf->buf[1], pbuf->buf[2],
               pbuf->buf[3], pbuf->buf[4], pbuf->buf[5]);

        //刷新缓冲区
        fflush(stdout);

        V(MUTEX);
        V(FULL);
    }
}

//消费者
void useCustomerProcess(HANDLE MUTEX, HANDLE EMPTY, HANDLE FULL, buffer *pbuf) {
    for (int i = 0; i < CONNUM; ++i) {
        P(FULL);
        Sleep(getRandomSleep());
        P(MUTEX);

        //消费数据
        char string[STRLEN + 1];
        memset(string, 0, sizeof(string));
        strcpy(string, pbuf->buf[pbuf->head]);
        memset(pbuf->buf[pbuf->head], 0, sizeof(pbuf->buf[pbuf->head]));
        pbuf->head = (pbuf->head + 1) % BUFSIZE;

        //打印时间
        time_t t = time(nullptr);
        struct tm *ptm = localtime(&t);

        //打印消费数据、时间和缓冲区映像
        printf("\nCustomerID:%6d, time: %d:%d:%d, gets data:%-10s\tbuffer 1:%-10s\t2:%-10s\t3:%-10s\t4:%-10s\t5:%-10s\t6:%-10s",
               (int) GetCurrentProcessId(),
               ptm->tm_hour, ptm->tm_min, ptm->tm_sec,
               string, pbuf->buf[0], pbuf->buf[1], pbuf->buf[2],
               pbuf->buf[3], pbuf->buf[4], pbuf->buf[5]);

        //刷新缓冲区
        fflush(stdout);

        V(EMPTY);
        V(MUTEX);
    }
}

//子进程程序
void useChildProcess(int type) {
    //打开信号量
    HANDLE MUTEX = OpenSemaphore(SEMAPHORE_ALL_ACCESS,
                                 FALSE,
                                 "mutex");
    HANDLE EMPTY = OpenSemaphore(SEMAPHORE_ALL_ACCESS,
                                 FALSE,
                                 "empty");
    HANDLE FULL = OpenSemaphore(SEMAPHORE_ALL_ACCESS,
                                FALSE,
                                "full");

    //打开共享内存区,并加载到当前进程地址空间
    HANDLE hmap = OpenFileMapping(
            FILE_MAP_ALL_ACCESS,
            FALSE,
            filemapping_name);
    LPVOID pFile = MapViewOfFile(
            hmap,
            FILE_MAP_ALL_ACCESS,
            0,
            0,
            0);
    buffer *pBuf = reinterpret_cast<buffer *>(pFile);

    //根据参数判断子进程是生产者或消费者
    if (type == PRO_TYPE) {
        useProducerProcess(MUTEX, EMPTY, FULL, pBuf);
    } else if (type == CON_TYPE) {
        useCustomerProcess(MUTEX, EMPTY, FULL, pBuf);
    } else {
        printf("maybe child process error!\n");
        exit(-1);
    }

    //解除映射
    UnmapViewOfFile(pFile);
    pFile = nullptr;
    CloseHandle(pFile);
    //关闭信号量
    CloseHandle(MUTEX);
    CloseHandle(EMPTY);
    CloseHandle(FULL);
}

int main(int argc, char *argv[]) {
    if (argc > 1) {
        //有参数则为子进程
        srand(GetCurrentProcessId());
        int type = atoi(argv[1]);
        useChildProcess(type);
    } else {
        //无参数则为主进程
        useParentProcess();
        system("pause");
    }
    return 0;
}

四、实验结果及分析

将以上代码编译运行后得到以下结果:
在这里插入图片描述
本次实验中,通过进程模拟生产者和消费者:
生产者进程ID:6172、1200
消费者进程ID:520、11216、11228
共计5个子线程,完成24次字符串生产和24次字符串消费.

五、实验收获与体会

“生产者消费者问题”是信号量知识里非常经典的案例,通过本次实验,我进一步加深了对进程概念的理解,深刻认识到并发执行的本质,理解和掌握Windows进程通信系统调用的功能,掌握了使用信号量机制完成进程间同步和互斥的方法。

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值