管程(信号量,互斥锁)

Monitor 介绍

管程,本文是在程序设计的时候用到了管程的操作,所以简单做了一个小管程

管程的概念

管程算是一种设计模型或者设计规则,但是也没人规定具体应该怎么写

之前了解到,大概分为两种,以进入共享区的顺序或者规则来进行划分

下面是自己理解的管程的结构

  • 需要一个共享机制

    可以是共享内存呀,管道,消息等等能用于线程间通信的机制,管程怎么说都是得两个或者两个以上嘛

  • 需要控制共享区域的机制

    可以是信号量Semaphore,互斥锁Mutex,或者其他未曾了解过的机制

    用于控制多个线程对于共享区域的访问,以免发生访问错误问题

从认知上来说,以C++为例,我们可以理解为两个程序使用了同一个管程类,同时调用来进行通信,而管程相对于直接使用共享变量的好处就是所有的操作和共享机制可以进行封装,更加规范,好调整,可移植等等,反正就是好用

实例

本实例是自己的理解写的,可能和意义上的管程不太一样,可以酌情进行考虑

结构

  • 环境 Windows,VS2022

  • 包含的库目录

    #pragma once
    #include "stdafx.h"
    #include <windows.h>
    
  • 变量

    public:
    	HANDLE hMapFile;    // 新创建的文件映射对象的句柄
    	LPVOID lpBase;      // 内存映射视图
    	HANDLE m_semaphore_R;      
    	HANDLE m_semaphore_W;      
    	HANDLE m_semaphore;
    	HANDLE m_mutex;
    
    	HANDLE hFile;
    
    	char szBuffer[100];
    
  • 成员方法

    public:
    	Monitor();
    	~Monitor();
    
    	void MakeFileHandle(LPCWSTR fileName);
    	void MakeMutex();
    	void MakeSemphore();
    	void MakeFileMapping(LPCWSTR mapfileName);
    	void MakeMapView();
    	void Lock(HANDLE m_mutex);
    	void UnLock(HANDLE m_mutex);
    	void P(HANDLE m_semaphore);
    	void V(HANDLE m_semaphore);
    	void ReadWrite(int flag);
    
    	enum {
    		MODE_READ,
    		MODE_WRITE
    	};
    

    剖析

    1. 控制机制使用MutexSemaphore

      Semaphore实现PV操作,模拟生产者和消费者的操作

      Mutex可能没那么需要,是想用来控制互斥访问信号量的,这部分写的可能有问题

    2. 申请信号量用

      CreateMutex();

      CreateSemaphore();

      两个函数在网上能找到很多的教程,暂时不多罗嗦,主要是懒

      但是有一个大坑

      • CreateMutex为例,如果有一个同名Mutex,则不会抢占,而是会新加一个指向这个Mutex的引用,所以在连个程序里Create两次,并不会出现重复的问题
      • OpenMutex相比,也不会出现重复,但是会抢占控制权,简单来说就是改变当前Mutex的状态,这样两个程序创建的时候,就会出现,第一个创建之后,更新了状态,第二个Open的时候,不会在相应的基础上进行修改,而是直接抢占控制权,覆盖相应的状态
      • 以上两个为自己的理解,其实不是在Mutex发现的,而是在Semaphore发现的,发现后来Open的Semaphore状态总是和设计好的不一样,找了好长时间,偶然发现有个博主说了有关控制权的问题,所以猜测可能相关
      • 这里有个小技巧,比如两个程序使用同一个Semaphore,这时候初始变量不是很好控制,有时候会出现覆盖的情况(AB需要两个Semaphore实现同步,A需要Semaphore1的初始值为1,则AB申请的时候都是【1,0】,但事实是这样可能会出现奇怪的问题,可能和我操作有关),这样很简单就是可以所有的Semaphore在所有的地方都申请0,A需要1的话,就在同步操作开始之前题前V一次,这样实现起来可以省很多脑细胞,而且不会出现奇奇怪怪的怀疑人生的问题
    3. 具体的互斥操作

      A:
      	P(S_A);
      	Lock(Mutex);
      	operation();
      	UnLock(Mutex);
      	V(S_B);
      
      B:
      	P(S_B);
      	Lock(Mutex);
      	operation();
      	UnLock(Mutex);
      	V(S_A);
      
      

代码

Monitor.h

#pragma once
#include "stdafx.h"
#include <windows.h>

class Monitor
{
public:
	Monitor();
	~Monitor();

	void MakeFileHandle(LPCWSTR fileName);
	void MakeMutex();
	void MakeSemphore();
	void MakeFileMapping(LPCWSTR mapfileName);
	void MakeMapView();
	void Lock(HANDLE m_mutex);
	void UnLock(HANDLE m_mutex);
	void P(HANDLE m_semaphore);
	void V(HANDLE m_semaphore);
	void ReadWrite(int flag);

	enum {
		MODE_READ,
		MODE_WRITE
	};

public:
	HANDLE hMapFile;    // 新创建的文件映射对象的句柄
	LPVOID lpBase;      // 内存映射视图
	HANDLE m_semaphore_R;      
	HANDLE m_semaphore_W;      
	HANDLE m_semaphore;
	HANDLE m_mutex;

	HANDLE hFile;

	char szBuffer[100];
};

Monitor.cpp

#include "Monitor.h"

DWORD WINAPI ThreadWrite(LPVOID lpParam);
DWORD WINAPI ThreadRead(LPVOID lpParam);

Monitor::Monitor()
{
	m_mutex = NULL;
	m_semaphore_R = NULL;
	m_semaphore_W = NULL;
}

Monitor::~Monitor()
{
	if (m_mutex != NULL)
	{
		CloseHandle(m_mutex);
	}	
	if (m_semaphore_R != NULL)
	{
		CloseHandle(m_semaphore_R);
	}
	if (m_semaphore_W != NULL)
	{
		CloseHandle(m_semaphore_W);
	}

	UnmapViewOfFile(lpBase);
	CloseHandle(hMapFile);
}

void Monitor::MakeFileHandle(LPCWSTR fileName)
{
	if (fileName == NULL)
	{
		hFile = INVALID_HANDLE_VALUE;
	}
	else
	{
		hFile = CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	}
}

void Monitor::MakeMutex()
{
	m_mutex = OpenMutex(MUTEX_ALL_ACCESS, false, L"Mutex");

	if (!m_mutex) m_mutex = CreateMutex(NULL, false, L"Mutex");
}

void Monitor::MakeSemphore()
{
	//m_semaphore_R = OpenSemaphore(MUTEX_ALL_ACCESS, false, TEXT("Read"));
	//m_semaphore_W = OpenSemaphore(MUTEX_ALL_ACCESS, false, TEXT("Write"));

	//if (m_semaphore_R == NULL)
		m_semaphore_R = CreateSemaphore(NULL, 1, 1, TEXT("Read"));
	//if (m_semaphore_W == NULL)
		m_semaphore_W = CreateSemaphore(NULL, 1, 1, TEXT("Write"));
}

void Monitor::MakeFileMapping(LPCWSTR mapfileName)
{
	hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, NULL, mapfileName);
	if (!hMapFile)
	{
		if (hFile == INVALID_HANDLE_VALUE)
		{
			hMapFile = CreateFileMapping(
				INVALID_HANDLE_VALUE,       // 物理文件句柄
				NULL,                       // 默认安全级别
				PAGE_READWRITE,             // 可读可写
				0,                          // 高位文件大小
				4096,                   // 地位文件大小
				mapfileName                 // 共享内存名称
			);
		}
		else
		{
			hMapFile = CreateFileMapping(
				hFile,						// 物理文件句柄
				NULL,                       // 默认安全级别
				PAGE_READWRITE,             // 可读可写
				0,                          // 高位文件大小
				0,							// 地位文件大小
				mapfileName                 // 共享内存名称
			);
		}
	}
}

void Monitor::MakeMapView()
{
	lpBase = MapViewOfFile(
		hMapFile,            // 共享内存的句柄
		FILE_MAP_ALL_ACCESS, // 可读写许可
		0,
		0,
		4096
	);
}

void Monitor::Lock(HANDLE m_mutex)
{
	WaitForSingleObject(m_mutex, INFINITE);
}

void Monitor::UnLock(HANDLE m_mutex)
{
	ReleaseMutex(m_mutex);
}

void Monitor::P(HANDLE m_semaphore)
{
	WaitForSingleObject(m_semaphore, INFINITE);
}

void Monitor::V(HANDLE m_semaphore)
{
	ReleaseSemaphore(m_semaphore, 1, NULL);
}

void Monitor::ReadWrite(int flag)
{
	if (flag == MODE_WRITE) {
		P(m_semaphore_W);

		Lock(m_mutex);
		std::cout << szBuffer << std::endl;;
		strcpy((char*)lpBase, szBuffer);
		UnLock(m_mutex);

		V(m_semaphore_R);
	}
	else if (flag == MODE_READ)
	{
		P(m_semaphore_R);

		Lock(m_mutex);
		strcpy(szBuffer, (char*)lpBase);
		lpBase = NULL;
		UnLock(m_mutex);

		V(m_semaphore_W);
	}
}

//CreateThread(NULL, 0, ThreadWrite, this, 0, 0);
//CreateThread(NULL, 0, ThreadRead, this, 0, 0);

DWORD WINAPI ThreadWrite(LPVOID lpParam)
{
	Monitor* pM = (Monitor*)lpParam;
	pM->P(pM->m_semaphore_W);

	pM->Lock(pM->m_mutex);
	strcpy((char*)pM->lpBase, pM->szBuffer);
	pM->UnLock(pM->m_mutex);

	pM->V(pM->m_semaphore_R);

	return NULL;
}

DWORD WINAPI ThreadRead(LPVOID lpParam)
{
	Monitor* pM = (Monitor*)lpParam;
	pM->P(pM->m_semaphore_R);

	pM->Lock(pM->m_mutex);
	strcpy(pM->szBuffer, (char*)pM->lpBase);
	pM->lpBase = NULL;
	pM->UnLock(pM->m_mutex);
	
	pM->V(pM->m_semaphore_W);

	return NULL;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值