摘要:在多实例多线程情况下,ActiveX 组件的不同实例共享同一全局数据缓冲区,在改造集成面向过程开发的传统代码时必须修改代码以消除全部全局变量。针对该情况,使用线程局部存储技术实现全局变量的局部化,采用具有大量全局变量的实体仿真代码实现ActiveX封装。该技术已成功应用于基于工业以太网的多通道数控系统中。
关键词:数控系统;线程局部存储;组件对象模型;ActiveX 组件
1 概述
组件对象模型(CompONent Object Model, COM)是由美国微软公司提出的一种二进制代码互操作规范,ActiveX 是实现了一些特定接口(例如IDispatch)的标准COM 组件。
COM/ActiveX 规范已成为软件业内最重要的工业标准之一。
基于组件的软件构架方法通过重用已有的软件组件,可使软件开发者像搭积木一样快速构造应用软件,从而提高生产效率,使软件设计更加规范可靠。目前基于组件的软件开发方法已经在业界得到广泛应用。在数控系统中也使用组件技术实现加工仿真,但现有文献较少涉及多个ActiveX 组件实例的情况。ActiveX 组件采用类似Windows消息运行机制的单套间模型(Single Threaded Apartment, STA)来串行化对组件属性和方法的调用,即对ActiveX 组件的所有调用由COM 系统负责线程的同步。因此,该组件的调用是线程安全的。
COM 在STA 套间内的线程中创建一个隐藏窗口,将套间外的线程对这个对象的调用都转变成对隐藏窗口发送消息,并由隐藏窗口的消息处理函数来实际调用组件对象,从而实现STA 套间模型。
一个进程中的所有线程均处于同一虚拟地址空间,每个函数的局部变量在运行该函数的每个线程中都是唯一的,但静态和全局变量则被所有线程所共享。即在多个ActiveX 组件实例的情况下,ActiveX 组件的 STA 模型不能保证全局数据成员是线程安全的。
2 线程局部存储原理
线程局部存储(Thread Local Storage, TLS)是Win32 系统提供的一种简化多线程程序设计的底层基础技术,其实质是介入全局数据创建过程,建立并管理全局数据与线程的关联,使得全局数据为其关联线程所私有。TLS 原理如图1 所示。
每个进程拥有一组TLS 槽口(Slot),每个槽口用序号标识,Windows 2000 有1 088 个这样的槽口。线程通过API 函数可以分配TLS 槽口,在TLS 槽口存取数据,进程中使用同一个序号的不同线程可指向独立的局部堆内存中进行数据存储,即线程ID 和槽口号确定了一个二维空间映射,线程通过API 函数获得线程间相互独立的数据存储地址。
图 1 也表明了采用TLS 机制的具有2 个ActiveX 组件实例的运行时软件内存结构,进程分配了2 个TLS 索引值gdwTlsIndex1 和 gdwTlsIndex2,这2 个索引值代表了TLS槽口的序号,但不同线程按照相同的序号却得到2 个独立的局部堆地址,而这些数据在线程内却具有全局数据的可访问性,即每个线程有单独的全局数据拷贝,该数据对线程内的函数具有全局作用域。
Win32 系统中与TLS 有关的API 及用法如下:
(1)进程初始化时分配TLS 槽口:
DWORD gdwTlsIndex;gdwTlsIndex = TlsAlloc();
(2)调用TlsSetValue 保存数据:
LPVOID lpvBuffer;lpvBuffer = (LPVOID) LocalAlloc(LPTR, 256);
TlsSetValue(gdwTlsIndex, lpvBuffer); //保存存储区指针
(3)调用TlsGetValue 取数据:
LPVOID lpvData;lpvData = TlsGetValue(gdwTlsIndex); //取TLS 槽口中保存的存//储区指针
(4)调用TlsFree 释放槽口:
lpvBuffer = TlsGetValue(gdwTlsIndex);
LocalFree((HLOCAL) lpvBuffer); //释放存储区
TlsFree(gdwTlsIndex); //释放TLS 槽口