当前位置: 代码迷 >> 综合 >> 2020-10-29 实验四 进程同步与通信
  详细解决方案

2020-10-29 实验四 进程同步与通信

热度:95   发布时间:2024-03-10 01:01:47.0

实验四 进程同步与通信

  • 一、实验目的:
  • 二、实验环境:
  • 三、实验内容:
  • 四、心得体会:

一、实验目的:

1. 掌握基本的同步与互斥算法,理解P,V操作。
2. 理解生产者消费者模型,了解其它典型的同步互斥模型,如哲学家就餐、读者-写者模型等。
3. 学习使用Windows中基本的同步对象,掌握相关API的使用方法。
4. 了解Windows中多线程的并发执行机制,实现进程的同步与互斥。
5. Windows进程间通信的方法有很多,了解其中的典型类型,如命名管道、文件映射等,掌握进程间通信的基本原理。了解windows系统环境下的进程通信机制,熟悉windows系统提供的进程通信API。

二、实验环境:

VC6.0

三、实验内容:

(写出主要的内容)
PART 1 进程同步与互斥
1.使用临界区对象,模拟售票功能。
(1) 运行截图
1

(2) 在分析程序运行结果的基础上,增加一个函数,模拟退票功能,并在主函数中加入适当的语句。
代码:

#include <windows.h>#include <iostream>using namespace  std;DWORD WINAPI SellPro_1(
LPVOID lpParameter);DWORD WINAPI SellPro_2(
LPVOID lpParameter );DWORD WINAPI SellPro_3(
LPVOID lpParameter );int tickets=100;CRITICAL_SECTION  critical_sec; //定义关键区域void main(){
    HANDLE hThread1;HANDLE hThread2;HANDLE hThread3;InitializeCriticalSection(&critical_sec); //初始化关键区域hThread1=CreateThread(NULL,0,SellPro_1,NULL,0,NULL);hThread2=CreateThread(NULL,0,SellPro_2,NULL,0,NULL);hThread3=CreateThread(NULL,0,SellPro_3,NULL,0,NULL);CloseHandle(hThread1);CloseHandle(hThread2);CloseHandle(hThread3);Sleep(4000);}DWORD WINAPI SellPro_1(
LPVOID lpParameter ){
       while(TRUE){
       Sleep(1);EnterCriticalSection(&critical_sec);
//进入关键代码区域 if(tickets>0){
    cout<<"thread1 sell 
ticket : "<<--tickets<<endl;}elsebreak;LeaveCriticalSection(&critical_sec);
//离开代码关键区域}  return 0;}DWORD WINAPI SellPro_2(
LPVOID lpParameter){
       while(TRUE){
       Sleep(1);EnterCriticalSection(&critical_sec);
//进入关键代码区域 if(tickets>0){
    cout<<"thread2 sell 
ticket : "<<--tickets<<endl;}elsebreak;LeaveCriticalSection(&critical_sec);
//离开代码关键区域}    return 0;}DWORD WINAPI SellPro_3(
LPVOID lpParameter){
       while(TRUE){
       Sleep(3);EnterCriticalSection(&critical_sec);
//进入关键代码区域 if(tickets>0){
    cout<<"\treturn ticket :
"<<++tickets<<endl;}elsebreak;LeaveCriticalSection(&critical_sec);
//离开代码关键区域}    return 0;}

运行截图:
2

2.使用信号量对象模拟售票功能。
(1)运行截图:
3

(2) 将函数Thread_B中的语句Sleep(10);改为Sleep(20);,再分析程序运行结果。
运行截图:
4
分析:运行两次thread_A后再运行一次thread_B。

3 . 简单的生产者-消费者问题

(1)运行截图:
5

(2)修改程序,将每次产生的数据改为一个100之内的随机数。
修改的代码:

#include <stdio.h>#include <process.h>#include <windows.h>#include<stdlib.h>#include<time.h>int END_PRODUCE_NUMBER=1;int g_Buffer;CRITICAL_SECTION g_cs;HANDLE g_hEventBufferEmpty,g_hEventBufferFull;unsigned int __stdcall ProducerThreadFun(PVOID pM)    //生产者进程{
    int i;srand((unsigned int)time(NULL));for (;;){
    i=rand()%100;WaitForSingleObject(g_hEventBufferEmpty,INFINITE);EnterCriticalSection(&g_cs);g_Buffer=i;printf("生产者将数据%d放入缓冲区\n",i);LeaveCriticalSection(&g_cs);SetEvent(g_hEventBufferFull);Sleep(1000);END_PRODUCE_NUMBER ++;if(END_PRODUCE_NUMBER==10)break;}return 0;}unsigned int __stdcall ConsumerThreadFun(PVOID
pM)    //消费者进程{
    int flag=1;while(flag)   {
    WaitForSingleObject(g_hEventBufferFull,INFINITE);EnterCriticalSection(&g_cs);   printf("消费者从缓冲区中取出数据%d\n",g_Buffer);if(g_Buffer==END_PRODUCE_NUMBER)flag=0;LeaveCriticalSection(&g_cs);SetEvent(g_hEventBufferEmpty);Sleep(1000);if(END_PRODUCE_NUMBER==10)break;}return 0;}int main(){
    HANDLE
hThread[2];printf("生产者消费者问题\n");InitializeCriticalSection(&g_cs);g_hEventBufferEmpty=CreateEvent(NULL,FALSE,TRUE,NULL);g_hEventBufferFull=CreateEvent(NULL,FALSE,FALSE,NULL);hThread[0]=(HANDLE)_beginthreadex(NULL,0,ProducerThreadFun,NULL,0,NULL);hThread[1]=(HANDLE)_beginthreadex(NULL,0,ConsumerThreadFun,NULL,0,NULL);WaitForMultipleObjects(2,hThread,TRUE,INFINITE);CloseHandle(hThread[0]);CloseHandle(hThread[1]);CloseHandle(g_hEventBufferEmpty);CloseHandle(g_hEventBufferFull);DeleteCriticalSection(&g_cs);return 0;}

运行截图:
6

(3)修改程序,生产者进程中产生数据的个数不固定为20,如果无键盘输入事件,则一直循环产生数据,直到有键盘按下的操作才停止。相应的消费者进程中语句也需要修改。
提示:可使用kbhit( )函数判断有无键盘输入。
修改的代码:

#include <stdio.h>#include <process.h>#include <windows.h>#include<stdlib.h>#include<time.h>#include<conio.h>int END_PRODUCE_NUMBER=1;int g_Buffer;CRITICAL_SECTION g_cs;HANDLE g_hEventBufferEmpty,g_hEventBufferFull;unsigned int __stdcall ProducerThreadFun(PVOID pM)    //生产者进程{
    int i;srand((unsigned int)time(NULL));while(!kbhit(
)){
    i=rand()%100;WaitForSingleObject(g_hEventBufferEmpty,INFINITE);EnterCriticalSection(&g_cs);g_Buffer=i;printf("生产者将数据%d放入缓冲区\n",i);LeaveCriticalSection(&g_cs);SetEvent(g_hEventBufferFull);Sleep(1000);END_PRODUCE_NUMBER ++;}return 0;}unsigned int __stdcall ConsumerThreadFun(PVOID
pM)    //消费者进程{
    int flag=1;while(flag)   {
    WaitForSingleObject(g_hEventBufferFull,INFINITE);EnterCriticalSection(&g_cs);   printf("消费者从缓冲区中取出数据%d\n",g_Buffer);if(g_Buffer==END_PRODUCE_NUMBER)flag=0;LeaveCriticalSection(&g_cs);SetEvent(g_hEventBufferEmpty);Sleep(1000);}return 0;}int main(){
    HANDLE
hThread[2];printf("生产者消费者问题\n");InitializeCriticalSection(&g_cs);g_hEventBufferEmpty=CreateEvent(NULL,FALSE,TRUE,NULL);g_hEventBufferFull=CreateEvent(NULL,FALSE,FALSE,NULL);hThread[0]=(HANDLE)_beginthreadex(NULL,0,ProducerThreadFun,NULL,0,NULL);hThread[1]=(HANDLE)_beginthreadex(NULL,0,ConsumerThreadFun,NULL,0,NULL);WaitForMultipleObjects(2,hThread,TRUE,INFINITE);CloseHandle(hThread[0]);CloseHandle(hThread[1]);CloseHandle(g_hEventBufferEmpty);CloseHandle(g_hEventBufferFull);DeleteCriticalSection(&g_cs);return 0;}

运行截图:
7

4.在理解简单生产者-消费者程序的基础上,自己编程实现稍复杂的模型,将缓冲区改为能够存放若干个整数的数组,如int
g_Buffer[N]; 其中N为一个整型常量。一个生产者,两个消费者。算法流程参考教材。
代码:

#include <stdio.h>#include <process.h>#include <windows.h>#include<conio.h>const int END_PRODUCE_NUMBER=20;int g_Buffer[100];CRITICAL_SECTION g_cs;HANDLE g_hEventBufferEmpty,g_hEventBufferFull;unsigned int __stdcall ProducerThreadFun(PVOID pM)    //生产者进程{
    int i=1;while(!kbhit()){
    WaitForSingleObject(g_hEventBufferEmpty,INFINITE);EnterCriticalSection(&g_cs);g_Buffer[i]=i;printf("生产者将数据%d放入缓冲区\n",i);g_Buffer[++i]=i;printf("生产者将数据%d放入缓冲区\n",i);LeaveCriticalSection(&g_cs);SetEvent(g_hEventBufferFull);Sleep(1000);i++;}return 0;}unsigned int __stdcall ConsumerThreadFun(PVOID pM)    //消费者进程{
    int flag=1;while(flag)   {
    static int i=1;WaitForSingleObject(g_hEventBufferFull,INFINITE);EnterCriticalSection(&g_cs);      printf("消费者从缓冲区中取出数据%d\n",i);if(g_Buffer[i]==END_PRODUCE_NUMBER)flag=0;LeaveCriticalSection(&g_cs);SetEvent(g_hEventBufferEmpty);Sleep(1000);i++;}return 0;}int main(){
    HANDLE hThread[2];printf("生产者消费者问题\n");InitializeCriticalSection(&g_cs);g_hEventBufferEmpty=CreateEvent(NULL,FALSE,TRUE,NULL);g_hEventBufferFull=CreateEvent(NULL,FALSE,FALSE,NULL);hThread[0]=(HANDLE)_beginthreadex(NULL,0,ProducerThreadFun,NULL,0,NULL);hThread[1]=(HANDLE)_beginthreadex(NULL,0,ConsumerThreadFun,NULL,0,NULL);WaitForMultipleObjects(2,hThread,TRUE,INFINITE);CloseHandle(hThread[0]);CloseHandle(hThread[1]);CloseHandle(g_hEventBufferEmpty);CloseHandle(g_hEventBufferFull);DeleteCriticalSection(&g_cs);return 0;}

运行截图:
8

PART 2 进程通信
共享内存文件映射方式
(1)运行截图:
9

(2)实现上述程序的控制停止功能,可以两种方式停止发送字符串:一种是服务器端发送10个字符串后便结束程序;另一种是用户在服务器端输入字符即结束程序。
修改的代码:

#include <iostream>#include <windows.h>#include <stdlib.h>#include <time.h>#include <conio.h>using namespace std;int  main(int argc, char *argv[]){
    int nRetCode = 0;char szBuffer[20] ;char c[20];HANDLE hMapping = CreateFileMapping(NULL,NULL,PAGE_READWRITE,0,4096,
"ShareMemory");LPVOID lpBase =
MapViewOfFile(hMapping,FILE_MAP_WRITE|FILE_MAP_READ,0,0,0);srand((unsigned)time(NULL));while(1){
       for(int i=0;i<=18;i++)szBuffer[i]=rand()%26+65;szBuffer[19]='\0';puts(szBuffer);strcpy((char*)lpBase,szBuffer);Sleep(1000);if(kbhit()){
    gets(c);if(strlen(c)==10)break;}}Sleep(20000);UnmapViewOfFile(lpBase);CloseHandle(hMapping);return nRetCode;}

运行截图:
10

(3)将上述程序的功能改为:server端每次发送两个100之内的整数,client端实现将两个整数相加,并输出加法计算式。
修改的代码:

Server:#include <iostream>#include <windows.h>#include <stdlib.h>#include <time.h>using namespace std;int  main(int argc, char *argv[]){
    int nRetCode = 0;char szBuffer[3] ;HANDLE hMapping =
CreateFileMapping(NULL,NULL,PAGE_READWRITE,0,4096, "ShareMemory");LPVOID lpBase =
MapViewOfFile(hMapping,FILE_MAP_WRITE|FILE_MAP_READ,0,0,0);srand((unsigned)time(NULL));while(1){
       szBuffer[0]=rand()%100;szBuffer[1]=rand()%100;szBuffer[2]='\0';printf("%d\t%d\n",szBuffer[0],szBuffer[1]);strcpy((char*)lpBase,szBuffer);Sleep(1000);}Sleep(20000);UnmapViewOfFile(lpBase);CloseHandle(hMapping);return nRetCode;}Client:#include <iostream>#include <windows.h>using namespace std;int  main(int argc, char *argv[]){
      int nRetCode = 0;HANDLE hMapping =
OpenFileMapping(FILE_MAP_ALL_ACCESS,NULL,"ShareMemory");if (hMapping){
    wprintf(L"%s\r\n",L"Success");LPVOID lpBase =
MapViewOfFile(hMapping,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);char szBuffer[20] = {
    0};   while(1){
     strcpy(szBuffer,(char*)lpBase);printf("%d+%d=%d\n",szBuffer[0],szBuffer[1],szBuffer[0]+szBuffer[1]);Sleep(1000);}UnmapViewOfFile(lpBase);CloseHandle(hMapping);}else{
    wprintf(L"%s",L"OpenMapping Error");}return nRetCode;}

运行截图:
11

四、心得体会:

   通过这节实验课,我懂得了关于基本的同步和互斥算法。理解到了很多关于p,v的操作,学会了使用Windows中基本的同步对象。掌握了Windows中的多线程的并发执行机制,可以实现进程之间的同步。了解到了Windows系统下的进程通信机制和进程通信API。
  相关解决方案