MFC中的TLS

Windows操作系统提供了Process/Thread的程序模型,其中Process是资源的分配对象
,掌握了程序所拥有的资源,而Thread则代表了程序的运行,是操作系统调度的对象。需
要注意,操作系统中,这两种东西都是一种KERNEL32对象。分别由Process DataBase和Th
read DataBase来表示。具体可以参考Matt Petrik的Windows 95 Programing Secret。

    Thread Local Storage是一个实现Thread的全局数据的机制,并且这些数据仅仅在这
个Thread中可见,因为这些数据保存在该Thread的Thread DataBase中:在每一个Thread
DataBase中都定义了一个64元的DWORD数组用来保存这些数据。同时操作系统也提供了相应
的函数来完成对这些数据的操作,如:TlsAlloc,TlsFree,TlsSetValue,TlsGetValue。

    在MFC中,也提供了TLS功能,为此MFC设计了一系列的类和程序来完成这个任务。具体
的程序在afxtls.cpp和afxtls_.h中。
涉及到的主要的类有:
class CTypedSimpleList : public CSimpleList
struct CThreadData : public CNoTrackObject
struct CSlotData
class CThreadSlotData
class CThreadLocal : public CThreadLocalObject
    其中CThreadSlotData是封装TLS的最重要的类,CTypedSimpleList,CSlotData,CTh
readDAta都是为了封装TLS而设计的只具有辅助功能的类。CThreadLocal是更高层的封装。

    首先让我们来对其数据封装方式进行分析,重要的类的定义及其分析如下所示:(为简
单起见,只列出数据成员而不再列出函数成员)
定义:
class CThreadSlotData
{
public:
DWORD m_tlsIndex;
int m_nAlloc;   
int m_nRover; 
int m_nMax;   
CSlotData* m_pSlotData;
CTypedSimpleList<CThreadData*> m_list;
CRITICAL_SECTION m_sect;
};
分析:
    在afxtls.cpp中定义了一个CThreadSlotData类的全局变量:_afxThreadData。在CTh
readLocal的成员函数中大量使用了这个全局变量来访问TLS功能。
DWORD m_tlsIndex
用来保存TLS数据的索引,也就是在Thread DataBase中64元数组中的偏移量,这个数据在
CThreadSlotData类的构造函数中初始化。
int m_nAlloc
int m_nRover
int m_nMax
这三个变量用来分配slot和记录相关状态,比如m_nAlloc用来保存当前已经分配的slot的
个数。线程为每一个TLS数据分配一个slot。
CSlotData* m_pSlotData;
用来记录已经分配的每一个slot的状态:已经使用或是尚未使用。
CTypedSimpleList<CThreadData*> m_list;
CThreadSlotData为每一个Thread实现一个并且只实现一个CThreadData对象,并且用链表
类对象m_list来管理它们。实际上,真正被保存到Thread DataBase中去的是这个CThread
Data对象的指针,而程序员要保存的TLS数据被保存到这个CThreadData对象的pData成员指
向的动态数组中。所有Thread的CThreadData对象通过CThreadData对象的pNext成员连成链
表,并由CTypedSimpleList<CThreadData*> m_list管理。
CRITICAL_SECTION m_sect;
由于所有Thread的TLS操作都要靠访问_afxThreadData来实现,这样就产成了多线程同步的
问题,m_sect就是用来进行线程同步的变量。保证每次只有一个Thread在访问_afxThread
Data中的成员变量。

定义:
struct CThreadData : public CNoTrackObject
{
CThreadData* pNext; // required to be member of CSimpleList
int nCount;         // current size of pData
LPVOID* pData;      // actual thread local data (indexed by nSlot)
};
分析:
    CThreadData用来辅助CThreadSlotData来完成TLS功能。每一个Thread的TLS数据都要
靠一个CThreadData对象来管理和保存。
CThreadData* pNext
在CThreadSlotData中,CThreadData由一个链表来管理,pNext用来把各个Thread的CThre
adData对象连成链表。
int nCount
指出用于保存TLS数据指针的动态数组的长度。
LPVOID* pData
在CThreadData保存的实际上是各个TLS数据的指针,为此定义了一个指针数组,nCount用
来指示数组长度,pData用来指出数组的基地址。

定义:
struct CSlotData
{
DWORD dwFlags;      // slot flags (allocated/not allocated)
HINSTANCE hInst;    // module which owns this slot
};
分析:
    CSlotData用来辅助CThreadSlotData来完成TLS功能。每一个Thread的TLS数据都要靠
一个CThreadData对象来保存,具体实现是把TLS数据的指针保存在CThreadData对象的动态
指针数组中(基地址由pData指出)。而这个数组中每一个成员的使用状况则由一个与之长度
相同的CSlotData数组来表示,具体由DWORD dwFlags来表明。

    从上面的分析不难发现,MFC中TLS功能的封装是这样的,所有Thread的TLS数据指针都
保存在一个动态的指针数组中,而该数组的基地址由一个CThreadData对象的 pData指出。
同时,保存在Thread DataBase中的是这个CThreadData对象的指针,而不是TLS数据的指针
,并且其索引值均相同,都为CThreadSlotData类中的m_tlsIndex成员。而且,在CThread
SlotData中提供了一个链表来管理所有Thread的CThreadData对象。这样CThreadSlotData
类就能访问所有的Thread的TLS数据。见图tls.bmp。(为了方便,我把图放到了签名档中了
,就在下面)


    下面来进一步说明如何使用TLS功能。
    为了方便TLS的使用,MFC设计了CThreadLocal类。它是一个模板类,具体的定义如下

template<class TYPE>
class CThreadLocal : public CThreadLocalObject
{
// Attributes
public:
AFX_INLINE TYPE* GetData()
{
TYPE* pData = (TYPE*)CThreadLocalObject::GetData(&CreateObject);
ASSERT(pData != NULL);
return pData;
}
AFX_INLINE TYPE* GetDataNA()
{
TYPE* pData = (TYPE*)CThreadLocalObject::GetDataNA();
return pData;
}
AFX_INLINE operator TYPE*()
{ return GetData(); }
AFX_INLINE TYPE* operator->()
{ return GetData(); }

// Implementation
public:
static CNoTrackObject* AFXAPI CreateObject()
{ return new TYPE; }
};
    在使用CThreadLocal时,只要用CThreadLocal<ClassType> name;即可构造一个类型为
ClassType的TLS数据,注意ClassType必须以CNoTrackObject为基类。实际上上述声明定义
了一个名称为name的CThreadLocal对象,但是通过这个CThreadLocal对象,即可生成并访
问类型为ClassType的TLS数据。

### 创建基于Maven构建的Spring Boot项目 #### 使用VSCode创建Spring Boot Maven项目 为了在Visual Studio Code (VSCode) 中创建一个新的Spring Boot Maven项目,需遵循一系列配置操作。确保已安装Java Development Kit (JDK),设置好环境变量以便命令行工具能够识别`java`和`mvn`命令。 安装必要的扩展来支持Java开发以及Maven项目管理,在VSCode市场中搜索安装以下两个官方推荐插件: - **Extension Pack for Java**: 提供完整的Java语言支持。 - **Maven for Java**: 增强对Maven项目的编辑体验。 完成上述准备之后,按照下面的方法启动新项目创建工作[^1]。 #### 初始化新的Maven项目 通过终端执行如下指令快速搭建基础框架: ```bash mkdir my-springboot-app && cd $_ mvn archetype:generate \ -DgroupId=com.example \ -DartifactId=my-springboot-app \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DinteractiveMode=false ``` 这段脚本会利用Maven Archetype机制自动生成标准目录结构与初始文件集。注意替换`com.example`为实际的应用程序包名,调整`my-springboot-app`以匹配期望的工程名称。 #### 添加Spring Boot依赖项 打开生成的`pom.xml`文件,向其中加入Spring Boot Starter Parent作为父POM,引入所需的Starters模块,比如Web服务支持等。修改后的XML片段可能看起来像这样: ```xml <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.0.0</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> ``` 保存更改后,VSCode中的Maven插件将会自动下载相应的库资源解析路径。 #### 配置运行参数 对于希望直接从IDE内部调试或测试应用的情况,可以在`.vscode/launch.json`内定义特定于Spring Boot应用程序的任务配置。例如: ```json { "version": "0.2.0", "configurations": [ { "type": "java", "name": "Launch MyApplication", "request": "launch", "mainClass": "com.example.MyApplication" } ] } ``` 这里假设主类名为`MyApplication.java`且位于指定包下;如果实际情况不同,则应相应地更新此字段值。 最后一步是在根目录下的`src/main/java/com/example/`位置新建一个简单的入口点实现——即包含`public static void main(String[] args)`方法的类文件,用于触发整个系统的初始化过程。 至此,已经成功建立了一个基本可用的Spring Boot Maven项目模板,可以直接编译、打包甚至部署至容器环境中进一步探索其功能特性了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值