首页 > 范文大全 > 正文

多进程间的数据通讯方式的探讨

开篇:润墨网以专业的文秘视角,为您筛选了一篇多进程间的数据通讯方式的探讨范文,如需获取更多写作素材,在线客服老师一对一协助。欢迎您的阅读与分享!

摘要:该文介绍了Windows环境下进行多进程编程的优缺点,分析了多进程编程时进程间通信的原理和多进程中资源共享的方法。重点讨论了如何在MFC中使用COPYDATA和共享虚拟内存方法实现进程间的数据通讯

关键词:多进程;MFC;COPYDATA;共享内存;数据通讯

中图分类号:TP311文献标识码:A文章编号:1009-3044(2012)08-1821-03

在Windows环境中,多线程编程的应用广泛,作用也很大。多进程与多线程编程相比,多进程存在系统资源占用大,启动慢,退出慢等缺点,但多进程也具有很多优点,进程的设计在彼此防护上非常严谨,即如果一个进程退出,在系统中其它进程还是可以继续执行的,这样的防护使得多进程编程比多线程编程健壮,所以在很多健壮性要求较高的系统软件中,多进程编程的方式也经常被采用。在Windows中,多线程之间数据搬移比较简单,我们可以采用消息队列,也可以采用进程间的全局变量来完成。多进程之间通信则相对来说比较复杂。[1]

1通过消息队列完成进程间数据转运

在多线程之间我们可以通过消息队列来完成数据通讯,其消息可以为系统消息也可为自定的消息。在多进程之间要通过消息队列来完成数据转运,则消息只有一个,名为WM_COPYDATA,其实它是专门用来完成线程间数据搬移的,只是该消息不管两个线程是否同属一个线程。

typedef struct tagCOPYDATASTRUCT{

ULONG_PTR dwData;

DWORD cbData;

PVOID lpData;

} COPYDATASTRUCT, *PCOPYDATASTRUCT;

在发送方,发送消息的时候必须要使用SendMessage(),不能使用PostMessage()或任何其它的变种函数如PostThreadMessage()这类函数,这是因为系统必须管理用以传递数据的缓冲区的生命周期。如果使用了PostMessage(),则数据缓冲区可能会在接收端线程处理该数据之前,被系统清除并摧毁。

下面本文将举例说明WM_COPYDATA使用方法,分为两个部分:一为接收端,二为发送端,下面通两个基于MFC对话框的程序举例说明[2]:

1)发送端部分代码

//数据发送部分

CString strOut;

GetDlgItemText(IDC_EDIT1,strOut);

COPYDATASTRUCT cds;

memset(&cds,0,sizeof(cds));

cds.dwData = 1001;

cds.cbData = strOut.GetLength()+1;

cds.lpData = (LPVOID)(LPCSTR)strOut;

CString szDisplayAppName = "testcopyDatarec";

CWnd *pDisplayWnd = CWnd::FindWindow(NULL,szDisplayAppName);

if(pDisplayWnd)

pDisplayWnd->SendMessage(WM_COPYDATA,(WPARAM)GetSafeHwnd(),(LPARAM)&cds);

//清除部分代码,只需要将COPYDATASTRUCT进行如果修改,其它部分同样处理即可

COPYDATASTRUCT cds;

memset(&cds,0,sizeof(cds));

cds.dwData = 1000;

cds.cbData = 0;

cds.lpData = NULL;

从上面的代码中我们可以看出,只要我们将想要转送的数据变为一个无类型的数据流赋予COPYDATASTRUST变量,再用SendMessage()函数发送即可,发送时要注意要获取接收端进程的应用程序名。

2)接收端部分代码

在接收端由于是接收并处理系统消息,首先则应该为窗口(线程)添加WM_COPYDATA消息响应函数,然后在消息响应函数中添加如下代码。

switch(pCopyDataStruct->dwData)

{case 1001://显示

{

CString strIn = (LPCSTR)(pCopyDataStruct->lpData);

SetDlgItemText(IDC_EDIT1,strIn);

CString strTemp;

strTemp.Format("接收数据为%d",strIn.GetLength()+1);

AfxMessageBox(strTemp);

strTemp.Format("实际传送数据为%d",pCopyDataStruct->cbData);

AfxMessageBox(strTemp);

break;

}

case 1000://清除

{CString strIn = (LPCSTR)(pCopyDataStruct->lpData);

strIn += "CLEAR";

SetDlgItemText(IDC_EDIT1,strIn);

break;

}

default:

{break;

}}

这样以来,我们可以轻松的完成多进程之间的数据交换。由于COPYDATA必须使用SendMessage()函数来完成消息发送,所以我们可以使用在一些数据交换频率不高的情况,如果数据交换频率过高,过多的使用SendMessage则可能会影响接收端消息泵对其它消息的处理,使接收端响应迟钝,影响正常使用。在这种情况下,我们则可以使用共享内存的方式来解决。

2使用共享内存完成数据通信

由于我们研制的单兵设备计算机性能不高,同时所配备的设备较多,又要求软件系统响应灵敏,系统稳定。为了提高系统的稳定性,我们将GPS,陀螺仪,激光测距机等设备的数据获取,采用进程来完成,以提高系统的高效可靠性能。同时由于数据获取的频率较高,所以在单兵设备软件系统中,我们采用共享内存的方式来完成数据交互。

在使用共享内存时,有三个步骤:(1)设定共享内存区域;(2)使用共享内存;(3)共享内存的清理。

2.1设定共享内存区域

使用CreateFileMapping()函数来创建一块具有给定名称的页面文件空间,使任何一个进程都可以根据其名称来存取到它。通过CreateFileMapping()可以产生出一个file-mapping核心对象,有了这个核心对象,再通过MapViewofFile()可以在共享内存中获得一个可用的内存指针,代码如下,

HANDLE hFileMapping;

LPDWORD pCounter;

hFileMapping = CreateFileMapping((HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,

0,sizeof(DWORD),”ServerThreadID”);

pCounter = (LPWORD)MapViewOfFile(hFileMapping,FILE_MAP_ALL_ACCESS,0,0,0);

*pCounter = GetCurrentThreadID();

这样我们就可以把当前程序的ThreadID放置在共享内存中。

2.2将共享区域映射到其它的进程地址空间中

如果把共享内存看作是Client/Server架构,那么Server进程才应该产生并初始化共享内存。所有的Client进程都应该使用OpenFileMapping()来读取指定的共享区域,再用MapViewOfFile()函数获得共享内存的指针,以读取共享内存中内容,代码如下。

HANDLE hFileMapping;

LPDWORD pCounter;

hFileMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS,TRUE,”ServerThreadID”);

pCounter = (LPWORD)MapViewOfFile(hFileMapping,FILE_MAP_ALL_ACCESS,0,0,0);

DWORD dwCurrentTheadID = *pCounter;

通过以上代码,可以读取刚才在Server进程中写入共享内存的数据,进而完成两个进程间数据的通讯。

2.3共享内存的清理

一旦完成了对共享内存的操作,应该及时的调用UnmapViewOfFile(),交出原来由MapViewOfFile()函数所获得的指针,再通过CloseHandle()关闭由CreateFileMapping()创建的file-mapping核心对象,这样就可以完成共享内存的清理工作。

通过以上的三个步骤,我们可以使用共享内存来完成进程之间的数据通讯。

3共享内存时数据的同步处理

在使用共享内存的时候,我们发现共享内存的过程,其实是一个同步读写一个file-mapping核心对象的过程,象共同读写一个文件时遇到的情况是类似的,因此在读与写的时候,我们必须要考虑数据同步的问题。由于读与写的动作在不同的进程中完成,所以最好的方法就是使用一个mutex互斥对象。例如,当写的muxtex处于lock状态时,读的操作则只能等待;待写的lock状态释放后,读的操作才能得以继续工作,相反,亦然。这样我们才能安全完成共享内存的读写操作。

4结束语

使用共享内存的方法,可以方便的完成进程间数据的搬移,但不是一个容易的事,要注意以下几点:1)尽量要使共享的数据量保持最小;2)不要把非一般类或类的集合放到共享内存中;3)严格的定义存取边界,不能超界。

能够正确使用共享内存的方式来完成进程间的数据通讯,我们则可以完成健壮的应用系统,当然你也可以使用消息队列或更高层次的进程通讯方法(IPC),甚至可以采用网络的方式来进行数据交换,在这里我们不做详细讨论。

参考文献:

[1] Beveridge J,Wiener R. Multithreading Applications in Win32[M].Addison-wesley Professional,2002.

[2]魏亮,,李春葆.Visual C++程序设计例学与实践[M].北京:清华大学出版社,2000.