预留内存携带附加信息的设计_第1页
预留内存携带附加信息的设计_第2页
预留内存携带附加信息的设计_第3页
免费预览已结束,剩余1页可下载查看

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、预留内存携带附加信息的设计有时候,将数据与一个对象的实例关联起来是很有帮助的。这种设计要求预 留一定的内存,一倍特定附加数据的存储。通过调用 SetWindowWord 或 SetWindowLong 函数将数据与一个指定的 窗口关联起来,数据保存在窗口附加内存块中。窗口内存块即是一种窗口对象 ( HWND)的附加数据(window extra bytes ),参考 WNDCLASS.cbWndExtra 字段( Specifies the number of extra bytes to allocate following the window instance.)。这种预留附加的设计,在

2、 MFC中处处可见。对于下拉选择列表 (CComboBox)、下拉列表框、列表视图和树控件,我们不光希望其能显示条 目内容( item text),还希望每个条目能够携带附加信息,即存储额外的关联数据( item data ),以备不时之需。这四个控件都提供了 SetItemData / GetItemData 接口,供用户储存关联数据。存储的数据为 DWORD值类型,可以是简单的数值,也可以存储指针。线程消息队列和 _ptiddata我们在编写第一个 SDK 窗口程序时,就接触到了消息这一重要概念。 实际上, 消息队列是一种线程私有数据,每一个 Windows 程序的 UI(CUI/GUI)

3、线程都维 持了一个消息队列。 GetMessage 、TranslateMessage、DispatchMessage等对消息的操作都是与调用线程的消息队列息息相关。 PostThreadMessage 是线程消息投递函数,它向一个指定 ID(idThread )的线程发送一条消息,然 后不等处理立即返回。这个 API 在多线程架构程序中非常有用。 PostQuitMessage 是结束线程运行, 相当于 nExitCode 作为 WM_QUIT 消息 参数调用 PostThreadMessage 。调用线程收到该消息后即 ExitThread ,故 该函数一般用来响应 WM_DESTROY消

4、息。尽管秉持封装的原则,我们极力强调避免使用全局变量,但全局变量对于进 程级和线程级的系统统筹管理却是非常有用。 除了消息队列这种系统内置的线程 私有数据外, Windows 提供了线程局部存储系统( TLS,Thread Local Storage ),为用户提供了存储与线程关联数据的接口。前面提到的 _beginthreadex 中分配的 _ptiddata ( pointer to per-thread data ), 即使用了 TLS。 _ptiddata 为 Windows 平台的多线程程序中, strtok 、 strerror (errno )等依赖全局变量或静态变量的 CRT

5、函数的实现提供了有效 的解决方案。Win32 线程局部存储系统用于管理 TLS 的数据结构是很简单的, Windows 仅为系统中的每一个进 程维护一个位数组,再为该进程中的每一个线程申请一个同样长度的数组空间, 如下图所示。在 Windbg 中,可以窥探 TEB 中的 TLS 数据结构。lkd> dt _tebnt!_TEB+0x02c ThreadLocalStoragePointer : Ptr32 Void+0xe10 TlsSlots: 64 Ptr32 Void+0xf10 TlsLinks: _LIST_ENTRY+0xf94 TlsExpansionSlots : Ptr

6、32 Ptr32 Voidtypedef struct_TEB / 66 elements, 0xFB8 bytes (sizeof)/ /*0x02C*/VOID*ThreadLocalStoragePointer;/ /*0xE10*/VOID*TlsSlots 64;/*0xF10*/ struct _LIST_ENTRY TlsLinks ; / 2elements, 0x8 bytes (sizeof)/ /*0xF94*/ VOID * TlsExpansionSlots ;/ TEB, * PTEB;当一个线程被创建时, Windows 就会在进程地址空间中为该线程分配一个 长度

7、为 TLS_MINIMUM_AVAILABLE 的数组,数组成员的值都被初始化为 0 。 在内部,系统将此数组与该线程关联起来, 保证只能在该线程中访问此数组中的 数据。如上图所示,每个线程都有它自己的数组,数组成员可以存储任何数据。运行在系统中的每一个进程都有上图所示的一个位数组。 位数组的成员是一 个标志,每个标志的值被设为 FREE 或 INUSE ,指示了此标志对应的数组索引 是否在使用中。 Windows 保证至少有 TLS_MINIMUM_AVAILABLE (定义在 WinNT.h 文件中)个标志位可用。动态使用 TLS 典型步骤如下。(1 )主线程调用 TlsAlloc 函数为

8、线程局部存储分配索引, 函数原型如下。 DWORDTlsAlloc ( VOID);TlsAlloc 为我们预订了一个索引。 如果 TlsAlloc 返回的索引为 3 ,那等 于说索引 3 已经被我们预订了, 无论是进程中当前正在运行的线程, 还是今后可 能会创建的线程,都不能再使用索引 3。(2)每个线程调用 TlsSetValue 和 TlsGetValue 设置或读取线程数组 中的值,这两个函数的原型如下。BOOL TlsSetValue (DWORDdwTlsIndex ,/ TLS indexLPVOIDlpTlsValue/ value to store);LPVOID TlsGe

9、tValue (DWORDdwTlsIndex/ TLS index);3)主线程调用 TlsFree 释放局部存储索引。函数的惟一参数是TlsAlloc 返回的索引BOOL TlsFree(DWORDdwTlsIndex/ TLS index);MFC中的线程局部存储如果你需要大量的数据贯穿一个线程, 普通的 TLS索引一个值就会变得不实用, Windows 的 TLS 只允许用户保存一个 32位的指针。如果需要用户保存任意类型的数据 (包含整个类) 。这个任意大小的数据所占的内存通常是在进程的 堆中分配, 所以当用户释放全局索引时, 系统必须将每个线程内此数据占用的内 存释放掉, 这就要求

10、系统把为各线程分配的内存都记录下来。 较好的方法是将各 个私有数据的首地址用一个链表连在一起, 释放全局索引时只要遍历此链表, 就 可以逐个释放线程私有数据占用的空间了。例如,有下面一个存放线程私有数据的数据结构。structCThreadDataCThreadData * pNext ; / 指向下一个线程的 CThreadData 结构的 指针LPVOID pData ; / 指向真正的线程私有数据的指针 ;指针 pData 指向为线程分配的内存的首地址, 指针 pNext 将各线程的数据 连在了一起。这实际上是一种 二级指针 的分槽存储。 MFC的线程局部存储类CThreadLocal

11、即实现 了二级指针的分槽存储。MFC框架的状态信息也是理解的难点,包括模块状态 AFX_MODULE_STAT、E 线程状态 _AFX_THREAD_STAT和E模块线程状态 AFX_MODULE_THREAD_STA。T这E 些线程级别的全局状态维持即使用了线程局部存储( TLS)。参考李久进著作的 MFC深入浅出第九章 MFC 的状态。由于 MFC广泛地应用了线程局部存储,故在 MFC下,使用线程必须格外小心。许多 MFC对象仅在创建它们的线程内运作。一般地,具有句柄映射的任何 对象都不能从其他线程访问该对象。例如,模块线程状态AFX_MODULE_THREAD_STA中TE的 CHand

12、leMap * m_pmapHWND映射记录了 MFC线程中创建的 CWnd 对象实例与内核窗口句柄( HWND)之间的映射消息。 内核窗口句柄是可以进程访问级别, 因此可跨线程访问。 但是试图传递 CWnd 对 象实例以期跨线程操作, 往往失败。 因为另一个引用线程并未像创建线程那样维 系一个映射, 所以当需要 CWnd HWND以执行 API 操作时,往往找不到其所指 窗口。针对以上问题, 通常优先传送句柄, 避免在线程之间传送 MFC对象。在引用 线程中将其转换为临时 MFC对象。例如,假设线程 A 创建一个 CWnd对象。线 程 A 并不将对象传送给线程 B,而将该对象的 m_hWnd 成员传送给线程 B。于是, 线程 B 可以调用 CWnd:FromHandle ,以创建一个临时的 CWnd 对象。如果线 程 B 需要更持久的连

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论