Windows多进程的内存共享和同步

本文介绍了在Windows平台上,使用FileMap进行多进程间内存共享,并配合Event实现内容变更的自动同步,以及在MFC对话框中测试的C++代码片段,讨论了Event同步机制的不确定性以及对多进程读写保护的必要性。
摘要由CSDN通过智能技术生成

在Windows平台下,使用FileMap实现多进程间的内存共享,同时配合使用Event实现内容变更的自动同步,使用MFC对话框界面测试功能,以下代码只截取部分关键的代码时间内容,舍去了部分界面相关的处理代码;

//.h文件内容

class CProcessMemSharedDlg : public CDialogEx
{
public:
	CProcessMemSharedDlg(CWnd* pParent = nullptr);	// 标准构造函数
	virtual ~CProcessMemSharedDlg();

private:
	void InitSharedMem();
	void UninitSharedMem();
	void ReadSharedMem();
	void WriteSharedMem();
	void ThreadFuncWaitChange();

private:
	HANDLE m_hSharedMem;
	int    m_nSharedMemSize;
	void*  m_pSharedMem;
	HANDLE m_eventSharedMemChange;
	std::thread m_threadWaitChange;
	std::atomic<bool> m_isRun;
};
//.cpp文件内容

CProcessMemSharedDlg::CProcessMemSharedDlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_PROCESSMEMSHARED_DIALOG, pParent),
	m_hSharedMem(INVALID_HANDLE_VALUE), m_pSharedMem(nullptr), m_nSharedMemSize(1024),
	m_eventSharedMemChange(INVALID_HANDLE_VALUE), m_isRun(false)
{
}
CProcessMemSharedDlg::~CProcessMemSharedDlg()
{
	UninitSharedMem();
}

void CProcessMemSharedDlg::InitSharedMem()
{
	m_hSharedMem = OpenFileMapping(FILE_MAP_ALL_ACCESS, NULL, "ProcessMemShared");//先尝试打开

	if (m_hSharedMem == INVALID_HANDLE_VALUE || m_hSharedMem == nullptr)//失败返回的是nullptr,算接口bug
	{
		m_hSharedMem = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, m_nSharedMemSize, "ProcessMemShared");
		if (m_hSharedMem == INVALID_HANDLE_VALUE)
		{
			MessageBox(_T("Error"), _T("CreateFileMapping Failed!"), MB_OK);
			return;
		}
	}

	m_pSharedMem = MapViewOfFile(m_hSharedMem, FILE_MAP_ALL_ACCESS, 0, 0, 0);
	m_eventSharedMemChange = OpenEvent(EVENT_ALL_ACCESS, false, "ProcessMemSharedEvent");
	if (m_eventSharedMemChange == INVALID_HANDLE_VALUE || m_eventSharedMemChange == nullptr)//失败返回的是nullptr,算接口bug
	{
		//关于使用Event做数据修改同步事件的问题
		//1.自动模式时,多个进程每次SetEvent只会触发一个进程的同步,即只有一个WaitForSingleObject()可以得到通知,之后就会重置事件
		//2.手动模式时,可以使所有进程同步触发,不ResetEvent()会一直触发,但是ResetEvent()的调用时机逻辑不好处理,考虑可以配合信号机实现Reset时机的逻辑判断
		m_eventSharedMemChange = CreateEvent(NULL, true, false, "ProcessMemSharedEvent");//内容修改同步事件
	}
	m_isRun = true;
	m_threadWaitChange = std::thread(&CProcessMemSharedDlg::ThreadFuncWaitChange, this);
}

void CProcessMemSharedDlg::ThreadFuncWaitChange()
{
	while (m_isRun)
	{
		//这里如果等待时间是INFINITE时,退出时需要等到新的Event触发才能结束,会导致线程无法jion;所以最好增加等待时间
		//WaitForSingleObject(m_eventSharedMemChange, INFINITE);
		if (WaitForSingleObject(m_eventSharedMemChange, 2000) == WAIT_OBJECT_0)
		{
			ReadSharedMem();

			//这里调用ResetEvent,测试效果是所有进程都可以触发,应该是异步Reset时序在其他进程Wait触发之后,这种方式的安全性有待考量
			ResetEvent(m_eventSharedMemChange);
		}
	}
}

void CProcessMemSharedDlg::UninitSharedMem()
{
	m_isRun = false;

	//这里线程退出会有问题,这个线程不能正常结束
	if (m_threadWaitChange.joinable())
		m_threadWaitChange.join();

	if (m_eventSharedMemChange != INVALID_HANDLE_VALUE)
	{
		CloseHandle(m_eventSharedMemChange);
		m_eventSharedMemChange = INVALID_HANDLE_VALUE;
	}

	if (m_pSharedMem != nullptr)
	{
		UnmapViewOfFile(m_pSharedMem);
		m_pSharedMem = nullptr;
	}
	if (m_hSharedMem != INVALID_HANDLE_VALUE)
	{
		CloseHandle(m_hSharedMem);
		m_hSharedMem = INVALID_HANDLE_VALUE;
	}
}

void CProcessMemSharedDlg::ReadSharedMem()
{
	if (m_pSharedMem != nullptr)
	{
		CString str((char*)m_pSharedMem);
		((CEdit*)GetDlgItem(IDC_EDIT_INFO))->SetWindowText(str);
	}
	else
		((CEdit*)GetDlgItem(IDC_EDIT_INFO))->SetWindowText("");

}

void CProcessMemSharedDlg::WriteSharedMem()
{
	if (m_pSharedMem != nullptr)
	{
		CString str;
		((CEdit*)GetDlgItem(IDC_EDIT_SEND))->GetWindowText(str);
		memcpy(m_pSharedMem, str.GetBuffer(), str.GetLength() + 1);//+1为了复制\0

		SetEvent(m_eventSharedMemChange);
	}
}

以上的实现是单个程序多开实现进程间内存共享的测试,所以会同时存在Create和Open的调用,如果是主从两个程序的话,只需在主程序中Create,副程序中Open即可。

关于同步机制使用Event是有点欠妥的,存在不确定性,不是一个很好的方案,实际项目中condition_variable应该是更优选(这里有待确认是否支持多进程)。

关于多进程的读写保护没有做处理,实现项目中最好增加相关的限制,比如增加Mutex或者读写锁作读写保护;以确保数据安全性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值