线程间通信

37
线线线线线 线线线线线

Upload: raven-parrish

Post on 03-Jan-2016

25 views

Category:

Documents


0 download

DESCRIPTION

线程间通信. 第四章 线程间通信. 4.1 进程/线程通信的方式 4.2 使用事件进行通信 4.3 Windows 消息 4.4 Shared Memory. 4.1 进程 / 线程通信的方式. 某些应用程序中,进程 / 进程和线程 / 线程之间不可避免地进行通信,进行消息传递,数据共享等 同一进程的线程之间通信方式包括 Windows 中常用 Event, Message 等。 - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 线程间通信

线程间通信线程间通信线程间通信线程间通信

Page 2: 线程间通信

4.2 X.J.Lee ©2008Windows 编程-多任务编程

第四章 线程间通信第四章 线程间通信

4.1 进程/线程通信的方式

4.2 使用事件进行通信

4.3 Windows 消息

4.4 Shared Memory

Page 3: 线程间通信

4.3 X.J.Lee ©2008Windows 编程-多任务编程

4.1 4.1 进程进程 // 线程通信的方式线程通信的方式 某些应用程序中,进程 /进程和线程 /线程之间不可避免地进行通信,进行消息传递,数据共享等 同一进程的线程之间通信方式包括Windows中常用

Event, Message等。 不同进程之间的通信可以利用 Event, FileMapping(内存共享 ), WM_COPYDATA消息以及 ClipBoard( 剪贴板 ), DDE( 动态数据交换 ), MessagePipe, MailSlot( 邮件槽)等

Page 4: 线程间通信

4.4 X.J.Lee ©2008Windows 编程-多任务编程

4.2 4.2 使用事件进行通信使用事件进行通信 事件除了用来保证两个线程之间同步之外,借由通知功能,也可以作为线程之间的简单通信的手段 线程使用WaitForSingleObject等待一件事情的发生,该事件可以由另外一个线程通过 SetEvent进行触发。

使用WaitForMultipleObjects等待多个由其他线程触发的事件(某些情况下,在线程的函数中也给自己发送消息)

事件只能通知一件事情的发生,不能传递其他附属数据。

Page 5: 线程间通信

4.5 X.J.Lee ©2008Windows 编程-多任务编程

通知事件

调用 SetEvent, 可以将事件的内核对象的状态变成已通知 调用 ResetEvent, 可以将事件的内核对象的状态变成未通知 调用 PulseEvent, 将事件对象置为有信号状态,然后立即置为无信号状态,在实际开发中这个函数很少使用

不同进程之间也可以通过 Event通知事件,这可以通过命名对象命名对象来实现 CreateEvent时指定了名称,不同进程之间通过创建同名的 Event

就可以获得指向同一个内核对象的句柄,这样不同进程间就可以互相通知和等待 Event

Page 6: 线程间通信

4.6 X.J.Lee ©2008Windows 编程-多任务编程

使用事件通知并传送数据 Event可以通知一件事情 , 但是不能同时传送数据 , 可以采用几种方式异步地获得数据 .

先设置给接收方数据 , 后通知 Event.

由接收 Event方提供设置 (保存 )数据的函数 , 在接收方接受到通知后 , 从自己保存的数据中取得数据 .

先通知 Event后从发送方取得数据 使用 Event做成消息队列

在接收方做成一个存储消息和数据的队列 , 每次登录一个消息 (数据 ) 后 ,通知接收方 , 接受方从该队列中顺序取得数据 .

Page 7: 线程间通信

4.7 X.J.Lee ©2008Windows 编程-多任务编程

4.3 Windows4.3 Windows 消息消息 Windows是一个消息(Message)驱动系统

Windows的消息提供了应用程序之间、应用程序与Windows系统之间进行通信的手段。

应用程序想要实现的功能由消息来触发,并且靠对消息的响应和处理来完成。

Windows 系统中有两种消息队列:系统消息队列和应用程序消息队列。 计算机的所有输入设备由Windows监控。当一个事件发生时,Windows先将输入的消息放入系统消息队列中,再将消息拷贝到相应的应用程序消息队列中。应用程序的消息处理程序将反复检测消息队列,并把检测到的每个消息发送到相应的窗口函数中。这便是一个事件从发生至到达窗口函数必须经历的过程。

注意:注意: 消息并非是抢占性的,无论事件的缓急,总是按照到达的先后派对,依次处理(一些系统消息除外),这样可能使一些实时外部事件得不到及时处理

Page 8: 线程间通信

4.8 X.J.Lee ©2008Windows 编程-多任务编程

用户界面线程和工作线程 Windows中存在两种线程:用户界面线程、工作线程。

工作线程工作线程 用来执行某些辅助处理的线程,它不需要进行任何系统事件或者窗口事件的处理。

用户界面线程用户界面线程 指拥有自己的消息循环并能对用户界面对象进行创建、交互和撤销的线程

Windows的消息机制与用户界面线程息息相关。 用户界面线程一般是继承 CWinThread类实现。

一个线程被创建后,系统假定线程不会被用于任何与用户相关的操作 一旦一个线程调用一个与图形用户界面有关的函数 (如创建窗口或者检

查消息队列的函数),Windows会分配给这个线程一个 THREADINFO结构。

Page 9: 线程间通信

4.9 X.J.Lee ©2008Windows 编程-多任务编程

线程的消息队列 每个线程利用 THREADINFO来认为自己是在一个独占的环境中运行。在这个结构里保存了一系列的消息队列(登记消息队列、发送消息队列、应答消息队列)、唤醒标志、以及用来描述线程局部输入状态的若干变量。

THREADINFO 结构是窗口消息系统的基础 当线程有了与之相联系的 THREADINFO的结构时,线程就有了自己的消息队列集合

Page 10: 线程间通信

4.10 X.J.Lee ©2008Windows 编程-多任务编程

Windows消息结构介绍 MSG

The MSG structure contains message information from a thread's message queue.

typedef struct tagMSG {

HWND hwnd; //接收消息的窗口句柄UINT message;

WPARAM wParam; //一个 32 位的参数,其含义和数值根据消息的不同而不同。

LPARAM lParam; //一个 32 位的消息参数,其值与消息有关。DWORD time; //消息放入消息队列中的时间POINT pt; //消息放入队列时的鼠标座标

} MSG, *PMSG;

用户使用自定义的消息的时候 , 可充分利用 lParam, 和 wParam传递数据

•message 消息类别。•这是一个数值,用以标识消息。对於每个消息息,均有一个对应的识别字,这些识别字定义在Windows 头文件中(其中大多数在WINUSER.H

中),以字首 WM开头。

•例如,使用者将鼠标窗口内,并按下左按钮,Windows就在消息队列中放入一个消息,该消息的message成员的值是WM_LBUTTONDOWN。这是一个常数,其值为0x0201。

Page 11: 线程间通信

4.11 X.J.Lee ©2008Windows 编程-多任务编程

将消息发送到线程的消息队列中 通过通过 PostMessagePostMessage 把消息发送给窗口所在的线程把消息发送给窗口所在的线程

The PostMessage function places (posts) a message in the message queue associated with the thread that created the specified window and returns without waiting for the thread to process the message.

BOOL PostMessage(

HWND hWnd, // handle to destination window

UINT Msg, // message

WPARAM wParam, // first message parameter

LPARAM lParam // second message parameter

);

当一个线程调用了这个函数的时候,系统要确定是哪一个线程建立了用hWnd 参数标志的窗口

系统分配内存,存储消息参数,将这块内存增加到相应线程的消息队列中 系统设置 QS_POSTMESSAGE 唤醒位标志

Page 12: 线程间通信

4.12 X.J.Lee ©2008Windows 编程-多任务编程

将消息发送到线程的消息队列中 通过通过 PostThreadMessage 将消息放置在线程的消息队列将消息放置在线程的消息队列

The PostThreadMessage function posts a message to the message queue of the specified thread. It returns without waiting for the thread to process the message.

BOOL PostThreadMessage(

DWORD idThread, // thread identifier

UINT Msg, // message

WPARAM wParam, // first message parameter

LPARAM lParam // second message parameter

);

可以通过 GetWindowThreadProcessID来确认是哪个线程创建了窗口 ThreadID是在线程创建的时候获得的,在全系统范围内是唯一的 要对线程编写消息循环( GetMessage , PeekMessage,DispatchMessage)

PostQuitMessage ( intnExitCode)向线程发送退出消息,等于PostThreadMessage(ThreadID,WM_QUIT,nExitCode,0);

Page 13: 线程间通信

4.13 X.J.Lee ©2008Windows 编程-多任务编程

PostMessage 和 SendMessage PostMessagePostMessage

当我们使用 PostMessage时,是将一个Message复制到“登记消息队列”中,然后并立即返回。之后由 GetMessage取回并响应之。

SendMessageSendMessage 当我们使用 SendMessage时,我们都知道,这个消息发送函数必须等到消息响应执行完毕才能返回。而它如何做到这一点的呢? 当调用这个 SendMessage的线程向这个线程自己创建的窗口发送消息的时候,它只是调用指定窗口的窗口过程,将其作为一个子例程,当窗口过程完成对消息的处理时,返回给 SendMessage一个值。

当一个线程向其他线程创建的窗口发送消息的时候, SendMessage首先将消息加入接收线程的“发送消息队列”,并为这个线程设置标志。同时发送线程将自己挂起,并在自己的“应答消息队列”中加入一个等待消息。当消息被接收线程处理完毕后,窗口的返回值被登记到发送线程的应答消息队列中。这时发送线程被唤醒,取出包含在应答消息队列中的返回值。

Page 14: 线程间通信

4.14 X.J.Lee ©2008Windows 编程-多任务编程

取得窗口消息 使用 GetMessage或者 PeekMessage取得窗口的消息

一个典型而普通的消息循环处理while(GetMessage(&msg,NULL,0,0))

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

这段处理必然是在用户界面线程的主循环中完成的 GetMessage的特点是当线程消息队列中有消息的时候则能立刻返回,否

则进行等待 PeedMessage也能从线程消息队列中取得消息,它的特点是不管消息队列是否存在消息,都不等待。

Page 15: 线程间通信

4.15 X.J.Lee ©2008Windows 编程-多任务编程

CWinApp处理消息

MFC应用程序都有一个 CWinApp对象,继承于CWinThread,在 CWinApp 的 Run函数中进行了和GetMessage 和 DispatchMessage的处理

可以在MFC 源代码 THRDCORE.cpp 找到CWinThread::Run()的代码,以及 Run函数的运行时机。

Page 16: 线程间通信

4.16 X.J.Lee ©2008Windows 编程-多任务编程

Page 17: 线程间通信

4.17 X.J.Lee ©2008Windows 编程-多任务编程

同时等待消息和内核对象 MsgWaitForMultipleObjects

某些情况下,你的线程不得不等待一个或多个事件,并且同时等待某些Message。

MsgWaitForMultipleObjects函数非常类似 WaitForMultipleObjects,但它会在“对象被激发”或者”消息到达队列”时被唤醒并且返回

DWORD MsgWaitForMultipleObjects(

DWORD nCount, // 等待内核事件的个数

LPHANDLE pHandles, // 内核事件对象数组指针

BOOLfWaitAll, // 是否等待全部事件才返回

DWORD dwMilliseconds, // 超时值

DWORD dwWakeMask // 唤醒 Mask

); 为了表达”消息到达队列“,返回值将是WAIT_OBJECT_0+nCount

•dwWakeMask指出了想要观察的用户输入Message

•QS_ALLINPUT

•QS_HOTKEY

•QS_INPUT

•QS_KEY

•QS_MOUSE

•QS_MOUSEBUTTON

•QS_MOUSEMOVE

•QS_PAINT

•QS_POSTMESSAGE

•QS_SENDMESSAGE

•QS_TIMER

Page 18: 线程间通信

4.18 X.J.Lee ©2008Windows 编程-多任务编程

MsgWaitForMultipleObjects 代码实例DWORD WINAPI ThreadFunc(LPVOIDlpParam){

boolbRun= true;while(bRun){

dwRet= MsgWaitForMultipleObjects(nEventCount,HandleArray,// event handle array FALSE,// 只要有一个事件触发,就可以返回dwMilliseconds,// 超时值QS_ALLINPUT );// 等全部消息

if (WAIT_FAILED == dwRet) {// 错误处理 }if (WAIT_TIMEOUT==dwRet ) {// 超时处理 }if (WAIT_OBJECT_0+nEventCount==dwRet) {

while(PeekMessage(&Msg,NULL,0,0,PM_REMOVE) {// 做消息处理 }} else{

switch ( dwRet-WAIT_OBJECT_0) {case EVENT_EVT1:// 处理事件 1break;//….. 其它事件处理

}}

}}

MsgWaitForMultipleObjects 代码实例DWORD WINAPI ThreadFunc(LPVOIDlpParam){

boolbRun= true;while(bRun){

dwRet= MsgWaitForMultipleObjects(nEventCount,HandleArray,// event handle array FALSE,// 只要有一个事件触发,就可以返回dwMilliseconds,// 超时值QS_ALLINPUT );// 等全部消息

if (WAIT_FAILED == dwRet) {// 错误处理 }if (WAIT_TIMEOUT==dwRet ) {// 超时处理 }if (WAIT_OBJECT_0+nEventCount==dwRet) {

while(PeekMessage(&Msg,NULL,0,0,PM_REMOVE) {// 做消息处理 }} else{

switch ( dwRet-WAIT_OBJECT_0) {case EVENT_EVT1:// 处理事件 1break;//….. 其它事件处理

}}

}}

Page 19: 线程间通信

4.19 X.J.Lee ©2008Windows 编程-多任务编程

Event 和 Message

Event会覆盖 (丢失 ), Message不会 Event只能通知一件事情的发生 ,不能传送数据 ,Message可以传递简单的数据 (通过 LParam 和 WParam)

在同一个进程内 , Message可以通过 LParam或者WParam指向一个堆内存而达到数据通信的功能 .

发 Event需要指定目标 Handle, 发 Message需要指定目标 Window(窗口句柄 )

Event可以跨越进程 , 通过名字达到共享 .

Message也可以跨越进程 , 通过注册消息 (RegisterWindowMessage),

在进程间共享消息 .

Page 20: 线程间通信

4.20 X.J.Lee ©2008Windows 编程-多任务编程

进程间通信-自定义消息 在某些情况下我们可能需要向其他的应用程序发送消息,这时候我们可以采用 SendMessage()函数向目标应用程序的某个窗口的句柄发送消息。 其中的技巧在于获取该窗口的句柄。 同时使用 RegisterWindowMessage()函数创建一个唯一的消息,并且两个应用程序相互都了解这条消息的含义。

同时还会用到 BrodcastSystemMessage()函数,它可以向系统中的每个应用程序的主窗口发送消息。这样便可以避免出现获取另一个应用程序窗口句柄的问题。 BroadcastSystemMessage()函数提供了附加的标志

BSF_LPARAMPOINTER,可以将写入参数 lParam的指针转化为可以被目标程序用来访问程序空间的指针,但是这个标志可能尚未进行文档标准化。

Page 21: 线程间通信

4.21 X.J.Lee ©2008Windows 编程-多任务编程

首先注册自己的窗口消息。 不过我们这次不用WM_USER+1的技术,注册窗口消息的好处是不必费心考虑 WM_USER 加上某个数之后,所表示的消息标识符是否超出工程的允许范围。本例在两个工程中都使用文本字符串来注册消息。由于这个文本字符串在整个系统中应当是唯一的,因此将使用一种称为 GUID 的 COM 技术来命名消息。 GUID名字生成器程序可以在 MFC 的 \BIN 目录下找到,其可执行文件名为 GUIDGEN.EXE。该程序将生成在应用程序已知范围内认为是唯一的文本字符串,这对应用程序来说当然是最好不过的。

Page 22: 线程间通信

4.22 X.J.Lee ©2008Windows 编程-多任务编程

1) 1) 注册一个唯一的窗口消息注册一个唯一的窗口消息 使用 GUIDGEN.EXE生成一个 GUID。 在应用程序中把 GUID定义为窗口消息文本字符串

#define HELLO_MSG “{6047CCB1-E4E7-11d1-9B7E-00AA003D8695}”

使用 ::RegisterWindowsMessage() 注册该窗口消息文本字符串 idHelloMsg= ::RegisterWindowMessage( HELLO_MSG );

保存消息标识符 idHelloMsg,便于以后使用。

Page 23: 线程间通信

4.23 X.J.Lee ©2008Windows 编程-多任务编程

2) 2) 向其他应用程序发送消息向其他应用程序发送消息 使用 ::RegisterWindowsMessage() 返回的消息标识符发送消息

::SendMessage(hWnd, idHelloMsg,wParam,lParam);

以上代码假定事先可以通过某种方式获取目标应用程序的某个窗口的句柄。一个指向 CWnd类的指针不能在程序范围之外而发挥作用。但是可以在 CWnd 类中封装已获取的窗口句柄,并如下所示来发送消息:

CWnd wnd;

wnd.Attach( hWnd );

wnd.SendMessage( idHelloMsg,wParam,lParam );

Page 24: 线程间通信

4.24 X.J.Lee ©2008Windows 编程-多任务编程

3) 3) 接收已注册的窗口消息接收已注册的窗口消息 为接收已注册的窗口消息,需要在接收窗口类,一般为 CMainFrame中手工添加 ON_REGISTERED_MESSAGE消息宏到消息映射中 :

BEGIN_MESSAGE_MAP( CMainFrame, CMDIFrameWnd)

// {{AFX_MSG_MAP( CMainFrame)

// }}AFX_MSG_MAP

ON_REGISTERED_MESSAGE( idHelloMsg,OnHelloMsg)

END_MESSAGE_MAP() 有关已注册消息的消息处理函数的代码如下:

LRESULT CMainFrame::OnHelloMsg( WPARAM

wParam,LPARAMlParam)

{

// process message

return 0;

}

Page 25: 线程间通信

4.25 X.J.Lee ©2008Windows 编程-多任务编程

该实例到目前为止,一直假定事先可以通过某种方式取得目标应用程序的某个窗口的句柄。但这是一个困难的任务。简单的方法是向每个应用程序广播一条消息,并且希望目标程序正在监听。由于在系统中注册了一条唯一的消息,因此只有目标程序会响应这条消息。应用程序广播的消息可能是它自己的窗口句柄,于是接收程序可以使用 ::SendMessage()来发送应答,也可能是用窗口句柄来结束循环。

Page 26: 线程间通信

4.26 X.J.Lee ©2008Windows 编程-多任务编程

4) 4) 广播窗口消息广播窗口消息WPARAM wParam= xxx;

LPARAM lParam= xxx;

DWORD dwRecipients= BSM_APPLICATIONS;

::BroadcastSystemMessage( BSF_IGNORECURRENTTASK,&

dwRecipients,idHelloMsg,

wParam,lParam);

Page 27: 线程间通信

4.27 X.J.Lee ©2008Windows 编程-多任务编程

进程间通信- WM_COPYDATA

通过 SendMessage可以把一个消息发送到另外一个进程,但是在另外一个进程中试图访问 LPARAM所指向的数据,也许会发生错误 进程地址空间是受到保护和相互隔离的,试图访问另外一个进程的地

址空间是不正确的。 Windows定义了WM_COPYDATA消息专门用来在线程之间传递数据 ,不管两个线程是否属于一个进程

处理WM_COPYDATA的线程必须有消息队列和窗口(带有消息队列的工作线程或者 UI 主线程 )

Page 28: 线程间通信

4.28 X.J.Lee ©2008Windows 编程-多任务编程

SendMessage(

hWndReceiver,

WM_COPYDATA,

(WPARAM)hWndSender,

(LPARAM)&cds //指向一个特定的Windows数据结构COPYDATASTRUCT

);

Typedef struct tagCOPYDATASTRUCT{

DWORD dwData; //通常用户自定义为行动代码

DWORD cbData; // lpData所指的数据块大小

PVOID lpData; // 一块数据,可以被传送到接受端

}COPYDATASTRUCT,*PCOPYDATASTRUCT;

•必须使用 SendMessage 而不是PostMessage发送WM_COPYDATA消息

•WM_COPYDATA所传送的数据可以在Heap 上也可以在 Stack 上申请,因为SendMessage保证接收方在返回前完成对数据的操作。

•接受方必须在WM_COPYDATA消息处理函数中完成对 lpData的处理(读取和保存其中的内容),而不是保存 lpData指针,对接收方来说,在这次消息处理后, lpData所指向的内存将不再是可用的。

Page 29: 线程间通信

4.29 X.J.Lee ©2008Windows 编程-多任务编程

4.4 Shared Memory4.4 Shared Memory Win32进程之间有严密的保护,一个进程要看到另外一个进程的地址空间中的任何一部分,都是不可能的。

程序只能见到逻辑的地址,所有进程的逻辑地址空间都是相同的 (4GB

的理论地址空间 )

WM_COPYDATA 技术简单但是效率不高 使用Win32进程通信技术的最低层 :共享内存 (SharedMemory)来进行

高效的进程间数据共享

Page 30: 线程间通信

4.30 X.J.Lee ©2008Windows 编程-多任务编程

使用共享内存的方法使用共享内存的方法 1.1. 设定一块儿共享内存设定一块儿共享内存

(1) 产生一个 FileMapping内核对象,指定共享区域大小( CreateFileMapping)

(2)将共享区域映射到你的进程的地址空间 (MapViewOfFile )

2.2. 使用共享内存使用共享内存 找到共享内存块

– 有些情况下,由 Server进程创建一个共享内存,其他进程只需要找到这块共享内存并使用就可以了。

– 1. 找到一个 FileMapping内核对象,使用 OpenFileMapping

» 系统找到同一个 FileMapping内核对象是根据对象的命名– 2. 将共享区域映射到你的进程的地址空间 (MapViewOfFile)

3.3. 同步处理共享内存同步处理共享内存 多个进程使用共享内存的时候,必须保证同步安全地使用共享的内存,通常使用等待被命名的Mutex来保证。

Page 31: 线程间通信

4.31 X.J.Lee ©2008Windows 编程-多任务编程

创建共享内存创建共享内存 CreateFileMappingCreateFileMapping HANDLE CreateFileMapping(

» HANDLE hFile, // 物理文件句柄» LPSECURITY_ATTRIBUTES lpAttributes, // 安全设置» DWORD flProtect, //保护设置» DWORD dwMaximumSizeHigh, // 高位文件大小» DWORD dwMaximumSizeLow, // 低位文件大小» LPCTSTR lpName //共享内存名称

);

Page 32: 线程间通信

4.32 X.J.Lee ©2008Windows 编程-多任务编程

1) 1) 物理文件句柄物理文件句柄 任何可以获得的物理文件句柄 , 如果你需要创建一个物理文件无关的内存映射也无妨 , 将它设置成为0xFFFFFFFF(INVALID_HANDLE_VALUE)就可以了 .

如果需要和物理文件关联 , 要确保你的物理文件创建的时候的访问模式和“保护设置”匹配 , 比如 : 物理文件只读 , 内存映射需要读写就会发生错误 . 推荐你的物理文件使用独占方式创建 .

如果使用 INVALID_HANDLE_VALUE, 也需要设置需要申请的内存空间的大小 , 无论物理文件句柄参数是否有效 , 这样CreateFileMapping就可以创建一个和物理文件大小无关的内存空间给你 , 甚至超过实际文件大小 , 如果你的物理文件有效 , 而大小参数为 0, 则返回给你的是一个和物理文件大小一样的内存空间地址范围 .返回给你的文件映射地址空间是可以通过复制 , 集成或者命名得到 ,

初始内容为 0.

Page 33: 线程间通信

4.33 X.J.Lee ©2008Windows 编程-多任务编程

2) 2) 保护设置保护设置 就是安全设置 , 不过一般设置 NULL就可以了 , 使用默认的安全配置 .

在 win2k下如果需要进行限制 , 这是针对那些将内存文件映射共享给整个网络上面的应用进程使用是 , 可以考虑进行限制 .

3) 3) 高位文件大小高位文件大小 目前我们的机器都是 32 位的 , 不可能得到超过 32 位进程所能寻址的

私有 32 位地址空间 , 一般还是设置 0.

4) 4) 低位文件大小低位文件大小 实际共享内存的大小 , 不过为了让其他共享用户知道你申请的文件映

射的相关信息 , 使用的时候是在获得的地址空间头部添加一个结构化描述信息 , 记录内存映射的大小 , 名称等。

5) 5) 共享内存名称共享内存名称 在其他进程创建同名的共享内存的时候,系统会返回存在的共享内存 .

Page 34: 线程间通信

4.34 X.J.Lee ©2008Windows 编程-多任务编程

映射共享内存-映射共享内存- MapViewOfFileMapViewOfFile

The MapViewOfFile function maps a view of a file into the

address space of the calling process.

LPVOID MapViewOfFile(

HANDLE hFileMappingObject, // handle to file-mapping object

DWORD dwDesiredAccess, // access mode

DWORD dwFileOffsetHigh, // high-order DWORD of offset

DWORD dwFileOffsetLow, // low-order DWORD of offset

SIZE_T dwNumberOfBytesToMap // number of bytes to map

);

•hFileMappingObject为CreateFileMapping() 返回的文件映像对象句柄。

•dwDesiredAccess 则再次指定了对文件数据的访问方式,而且同样要与CreateFileMapping()函数所设置的保护属性相匹配。

Page 35: 线程间通信

4.35 X.J.Lee ©2008Windows 编程-多任务编程

MapViewOfFile()函数允许全部或部分映射文件,在映射时,需要指定数据文件的偏移地址以及待映射的长度。其中, 文件的偏移地址由 DWORD 型的参数 dwFileOffsetHigh 和

dwFileOffsetLow 组成的 64 位值来指定,而且必须是操作系统的分配粒度的整数倍

– 对于Windows操作系统,分配粒度固定为 64KB。当然,也可以通过如下代码来动态获取当前操作系统的分配粒度:SYSTEM_INFO sinf;

GetSystemInfo(&sinf);

DWORD dwAllocationGranularity= sinf.dwAllocationGranularity;

MapViewOfFile的返回值就是被映射在进程地址空间中的共享内存块指针,用户操作这个指针可以达到共享内存了。

Page 36: 线程间通信

4.36 X.J.Lee ©2008Windows 编程-多任务编程

使用共享内存使用共享内存 -Create/OpenFileMapping-Create/OpenFileMapping 共享内存以点对点( peer to peer) 的形式呈现

则每个进程都必须有相同的能力,产生共享内存并将它初始化。每个进程都应该调用 CreateFileMapping(),然后调用 GetLastError(). 如果传回的错误代码是 ERROR_ALREADY_EXISTS, 那么进程就可以假设这一共享内存区域已经被别的进程打开并初始化了,否则该进程就可以合理的认为自己排在第一位,并接下来将共享内存初始化。

共享内存以 client/server 架构的形式呈现 只有 server进程才应该产生并初始化共享内存。 所有的进程都应该使用由 OpenFileMapping得到的共享内存映射文件。

– HANDLE OpenFileMapping(» DWORD dwDesiredAccess, // access mode» BOOL bInheritHandle, // inherit flag » LPCTSTR lpName // object name,是其他进程用 CreateFileMapping函数创建共享内存时指定的对象名称。

– ); 再调用MapViewOfFile(),取得共享内存的指针

Page 37: 线程间通信

The endThe end