进程同步与互斥实验

目录

基本操作

1.函数CreateThread,用于创建线程,声明如下:

2.函数CreateSemaphore,用于创建信号量,声明如下:

 3. 函数WaitForSingleObject,用于申请资源,声明如下:

4.函数ReleaseSemaphore,用于释放资源,声明如下:

5.函数WaitForMultipleObjects,用于等待多个内核对象,声明如下:

实验一(两人向一个账户存款问题)

问题描述

实验代码

运行结果

结果分析

实验二 (多个程序合作)

题目描述

实验代码

运行结果 

 结果分析

实验三(售票员和司机合作问题)

问题描述

实验代码

运行结果

结果分析

实验四(生产者\消费者问题)

问题描述

实验代码

运行结果 

结果分析


基本操作

1.函数CreateThread,用于创建线程,声明如下:

WINBASEAPI

     __out

     HANDLE

     WINAPI

CreateThread(

     __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,

     __in      SIZE_T dwStackSize,

     __in      LPTHREAD_START_ROUTINE lpStartAddress,

     __in_opt LPVOID lpParameter,

     __in     DWORD dwCreationFlags,

     __out_opt LPDWORD lpThreadId      );


lpThreadAttributes是线程的属性。

dwStackSize是线程的栈大小。

lpStartAddress是线程函数的开始地址。

lpParameter是传送给线程函数的参数。

dwCreationFlags是创建线程标志,比如挂起线程。

lpThreadId是标识这个线程的ID。

2.函数CreateSemaphore,用于创建信号量,声明如下:

CreateSemaphore(

     __in_opt LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,

     __in     LONG lInitialCount,

     __in     LONG lMaximumCount,

     __in_opt LPCSTR lpName   );

lpSemaphoreAttributes是信号量的安全属性。

lInitialCount是初始化的信号量。

lMaximumCount是允许信号量增加到最大值。

lpName是信号量的名称。

例如: m_hSemaphore = CreateSemaphore( //在vs中写 CreateSemaphoreA

                                                 NULL,   // 缺省的安全属性

                                                 0,   // 初始化为0个信号量

                                                 cMax,   // 最大为10个信号量

                                                  NULL); // 不命名

 3. 函数WaitForSingleObject,用于申请资源,声明如下:

WaitForSingleObject(

HANDLE hObject, //指明一个内核对象的句柄

DWORD dwMilliseconds); //等待时间

4.函数ReleaseSemaphore,用于释放资源,声明如下:

ReleaseSemaphore(

      __in      HANDLE hSemaphore,

      __in      LONG lReleaseCount,

      __out_opt LPLONG lpPreviousCount  );

hSemaphore是要增加的信号量句柄。

lReleaseCount是增加的计数。

lpPreviousCount是增加前的数值返回。

5.函数WaitForMultipleObjects,用于等待多个内核对象,声明如下:

DWORD WaitForMultipleObjects( 

            DWORD nCount,             

            CONST HANDLE *lpHandles,   

            BOOL fWaitAll,             

            DWORD dwMilliseconds       ); 

 nCount是句柄的数量,最大值为MAXIMUM_WAIT_OBJECTS(64)。 

 lpHandles是HANDLE句柄数组的指针,HANDLE 类型可以为(Event,Mutex,Process,Thread,Semaphore)数组。 

bWaitAll是等待的类型,如果为TRUE则等待所有信号量有效在往下执行,FALSE 当有其中一个信号量有效时就向下执行。 

 dwMilliseconds是超时时间,超时后向执行。如果为WSA_INFINITE永不超时。如果没有信号量就会在这死等。 

实验一(两人向一个账户存款问题)

问题描述

妈妈和孩子共享一个账户count,初始账户余额为100,妈妈进程程往count中存款100,孩子进程往count中存款200。count是临界资源,妈妈进程和孩子进程要互斥使用count。编写一个实现妈妈和孩子进程互斥的代码。

实验代码

#include <iostream>
#include <cstdlib>
#include <windows.h>
#include <stdio.h>
using namespace std;

DWORD WINAPI mother(LPVOID lpParameter);
DWORD WINAPI child(LPVOID lpParameter);
HANDLE mutex;  //API函数返回信号量句柄
int count=100;

int main()
{  
    HANDLE hThread[2];
    DWORD motherID; //妈妈线程的标识符
    DWORD childID; //孩子线程的标识符
    mutex = CreateSemaphore(NULL, 1, 1, NULL);  //缺省安全属性,初始资源为1,最大并发数为1,创建匿名信号量
    hThread[0] = CreateThread(NULL, 0, mother, 0, 0, &motherID);
    hThread[1] = CreateThread(NULL, 0, child, 0, 0, &childID);
    WaitForMultipleObjects(2, hThread, true, INFINITE);  //用于等待多个线程,2是线程个数,hThread是句柄数组的指针
    printf("count=:%d\n",count);

    return 0;
}

DWORD WINAPI mother(LPVOID lpParameter){  //LPVOID是一个void* 数据类型
    int n;
    WaitForSingleObject(mutex,INFINITE);  //INFINITE为无限时间量
    n=count;
    Sleep(100);
    n=n+100;
    count=n;
    ReleaseSemaphore(mutex, 1, NULL);  //信号量资源数加一

    return 0;
}

DWORD WINAPI child(LPVOID lpParameter){
    int m;
    WaitForSingleObject(mutex,INFINITE);
    m=count;
    m=m+200;
    Sleep(100);
    count=m;
    ReleaseSemaphore(mutex, 1, NULL);  //信号量资源数加一

    return 0;
}

运行结果

结果分析

         加入互斥信号量mutex,使得mother进程和child进程互斥的访问互斥资源count,从而程序既能实现并发,又能保证程序运行结果的可再现性。

实验二 (多个程序合作)

题目描述

如图所示,模拟它们的运行。

实验代码

#include <iostream>
#include <cstdlib>
#include <windows.h>
using namespace std;

DWORD WINAPI FunA(LPVOID lpParameter);
DWORD WINAPI FunB(LPVOID lpParameter);
DWORD WINAPI FunC(LPVOID lpParameter);
HANDLE g_ThreadSema1;
HANDLE g_ThreadSema2;

int main()
{
    HANDLE hThread[3];
    g_ThreadSema1 = CreateSemaphore(NULL, 0, 1, NULL); //创建匿名信号量,初始资源为零,最大并发数为1,
    g_ThreadSema2 = CreateSemaphore(NULL, 0, 1, NULL);
    hThread[0] = CreateThread(NULL, 0, FunA, 0, 0, NULL); // 创建线程
    hThread[1] = CreateThread(NULL, 0, FunB, 0, 0, NULL); // 创建线程
    hThread[2] = CreateThread(NULL, 0, FunC, 0, 0, NULL); // 创建线程
    WaitForMultipleObjects(3, hThread, true, INFINITE);  //用于等待多个线程,2是线程个数,hThread是句柄数组的指针

    return 0;
}

DWORD WINAPI FunA(LPVOID lpParameter){
    Sleep(1000);
    cout<<"$A"<<endl;    //表示创建线程A
        Sleep(500);
    cout<<"I am A"<<endl;    //表示线程A开始运行
    ReleaseSemaphore(g_ThreadSema1, 1, NULL);  //信号量资源数加一
    ReleaseSemaphore(g_ThreadSema2, 1, NULL);  //信号量资源数加一
    
    return 0;
}

DWORD WINAPI FunB(LPVOID lpParameter){
    cout<<"$B"<<endl;    //表示创建线程B
    Sleep(100);
        WaitForSingleObject(g_ThreadSema1, INFINITE); //等待信号量资源数>0 
    cout<<"I am B"<<endl;    //表示线程B开始运行
    Sleep(100);
    
    return 0;
}

DWORD WINAPI FunC(LPVOID lpParameter){
    Sleep(100);
        cout<<"$C"<<endl;    //表示创建线程C
        WaitForSingleObject(g_ThreadSema2, INFINITE); //等待信号量资源数>0
        Sleep(100);
    cout<<"I am C"<<endl;    //表示线程C开始运行
    
    return 0;
}

运行结果 

 结果分析

          A,B,C分别相对应于图中的P1,P2,P3;&B,&C,&A表示先后创建了进程B,C,A;由于进程B,C的运行需要等待A运行完释放的信号量才能继续运行。因此A先运行;A运行后,先释放B等待的信号量,然后B开始运行;后释放C等待的信号量,最后C运行。

实验三(售票员和司机合作问题)

问题描述

设公共汽车上,司机和售票员的活动分别是:

司机的活动:  启动车辆;

                       正常行车;

                       到站停车;

售票员的活动:关车门;

                         售票;

                        开车门;

编写程序实现这两个活动的同步关系。

实验代码

#include <iostream>
#include <windows.h>
#include <cstdlib>
using namespace std;

HANDLE close,stop;
DWORD WINAPI Drive(LPVOID lpParameter);
DWORD WINAPI Conducte(LPVOID lpParameter);

bool g_continue = true; //控制
int main() {
	close=CreateSemaphore(NULL,0,1,NULL);//创建关门信号量
	stop=CreateSemaphore(NULL,0,1,NULL);//创建停车信号量

	HANDLE Driver,Conductor;

	Driver = CreateThread(NULL, 0, Drive, 0, 0, NULL); // 创建司机线程
	Conductor = CreateThread(NULL, 0, Conducte, 0, 0, NULL); // 创建售票员线程

	while(g_continue) {
		if(getchar()) {
			g_continue=false;
		}
	}
	return 0;
}
//司机进程
DWORD WINAPI Drive(LPVOID lpParameter) {
	while(true) {
		WaitForSingleObject(close,INFINITE);
		cout<<"start the bus"<<endl;
		cout<<"run"<<endl;
		Sleep(1000);
		cout<<"stop the bus"<<endl;
		ReleaseSemaphore(stop,1,NULL);
	}
	return 0;
}
//售票员进程
DWORD WINAPI Conducte(LPVOID lpParameter) {
	while(true){
	cout<<"close the door"<<endl;
    cout<<"sale tickets"<<endl;
	ReleaseSemaphore(close,1,NULL);
	WaitForSingleObject(stop,INFINITE);
	cout<<"open the door"<<endl<<endl;
	}
	return 0;
}

运行结果

结果分析

        首先售票员进程需要先关上车门,售票后,发出关门信号,司机等到关门信号后,开始起动,开车,到站停车后,发出停车信号,售票员等到停车信号后,打开车门...如此循环下去。

实验四(生产者\消费者问题)

问题描述

假定在生产者和消费者之间的公用缓冲池中,具有n个缓冲区,这时可利用互斥信号量mutex实现诸进程对缓冲池的互斥使用;利用信号量empty和full分别表示缓冲池中空缓冲区和满缓冲区的数量。又假定这些生产者和消费者相互等效,只要缓冲池未满,生产者便可将消息送入缓冲池;只要缓冲池未空,消费者便可从缓冲池中取走一个消息。

•    Consumer_product.cpp

•    变量定义

   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是对象的句柄,最基本的句柄类型)

HANDLE g_hFullSemaphore;     //当缓冲区满时迫使生产者等待

HANDLE g_hEmptySemaphore;     //当缓冲区空时迫使消费者等待

DWORD WINAPI Producer(LPVOID);    //生产者线程(DWORD是无符号整型)

DWORD WINAPI Consumer(LPVOID);    //消费者线程

•    部分函数

CreateMutex();创建互斥信号量

CreateSemaphore ();创建同步信号量

CreateThread ();创建线程

WaitForSingleObject (); 等待信号量

 ReleaseMutex (); 释放信号量

 ReleaseSemaphore();

 Sleep();执行挂起一段时间

实验代码

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

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,SIZE_OF_BUFFER,NULL);
    g_hEmptySemaphore = CreateSemaphore(NULL,0,SIZE_OF_BUFFER,NULL);
    //调整下面的数值,可以发现,当生产者个数多于消费者个数时,
    //生产速度快,生产者经常等待消费者;反之,消费者经常等待
    const unsigned short PRODUCERS_COUNT = 3;  //生产者的个数
    const unsigned short CONSUMERS_COUNT = 1;  //消费者的个数
    
    //总的线程数
    const unsigned short THREADS_COUNT = PRODUCERS_COUNT+CONSUMERS_COUNT;
    HANDLE hThreads[PRODUCERS_COUNT]; //各线程的handle
    DWORD producerID[CONSUMERS_COUNT]; //生产者线程的标识符
    DWORD consumerID[THREADS_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 << " <-- produce";//生产
        if (i==out) std::cout << " <-- consume";//消费
        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 << " <-- produce";//生产
        if (i==out) std::cout << " <-- consume";//消费
        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;
}

运行结果 

 结果分析

         由于先创建了三个生产者和一个消费者,三个消费者并发执行消费者进程,生产3个产品并放在缓冲区后,释放一个empty信号量,消费者等到一个empty信号量后开始取产品使用,然后释放一个full信号量,生产者继续生产,然后消费者再取,依次交替进行下去。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值