在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或者读写锁作读写保护;以确保数据安全性。