COM 里面不支持TLS(Thread Local Storage) 吗?

jsjwql 2007-06-27 11:17:15
我在COM里面写了下面的代码出错了:
__declspec(thread) static int num = 321;

void test()
{
int i = num; // got an error " Access violation reading location 0x00000000."
num = 332211;
Sleep(5000);
i = num;
}
然而这个代码在普通的dll中可以运行。

如果com不支持的话,我想问下在com有个static的全局变量,如果想在com里面实现多线程,而且每个线程都能单独维护自己的这个static的全局变量,如何做到?
谢谢!
...全文
335 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
wwwhhb4001 2008-10-22
  • 打赏
  • 举报
回复

学习一下
jsjwql 2007-06-27
  • 打赏
  • 举报
回复
谢谢jiangsheng(蒋晟.Net[MVP]),这里没用到MFC库
蒋晟 2007-06-27
  • 打赏
  • 举报
回复
去看MFC的源代码
jsjwql 2007-06-27
  • 打赏
  • 举报
回复
谢谢!
taodm 2007-06-27
  • 打赏
  • 举报
回复
有没有com版啊,去那边问吧
如果没有com版就去windows版问吧。
这个不是C++话题。
jsjwql 2007-06-27
  • 打赏
  • 举报
回复
谢谢,现在豁然开朗了!
feimingbiao 2007-06-27
  • 打赏
  • 举报
回复
这个是我随手写的实现简化版,没有错误检查,没有扩展情况,程序也不规范,不过机制保证是对的。你可以Debug一下对比。:)

bool slotBitmap[64] = {0};

int TlsAlloc()
{
for (int i = 0; i < 64; ++i)
{
//找个空Index。
if (slotBitmap[i] == 0)
{
slotBitmap[i] = 1;
return i;
}
}
//爆了,扩展Slot
}

int TlsFree(DWORD i)
{
slotBitmap[i] = 0; //没查错。
}

TlsGetValue(DWORD index)
{
if (slotBitmap[index] == 0)
{
return NULL;
}
else
{
return GetCurrentPeb()->TlsSlot[index];
}
}

TlsSetValue(DWORD index, LPVOID lpTlsValue)
{
if (slotBitmap[index] == 0)
{
return FALSE;
}
else
{
GetCurrentPeb()->TlsSlot[index] = lpTlsValue;
return TRUE;
}
}
feimingbiao 2007-06-27
  • 打赏
  • 举报
回复
对不起我全说错了,这个东西我用的少,全理解错了,不应该信口开河。别看我上面的帖子,饿补了以下,以下是真正Tsl的实现。

我说的TlsSlots[64]那部分没有错,每个Thread在TEB里面有这个定义,每个元素是对应于TsAlloc返回的Index!(所以叫Index,唉)。当一个Thread调用TsAlloc的时候,就先找一个大家都没有用过的Index,然后所有的Thread共享这个Index(比如叫TsAlloc的Thread发现5是空的,就返回5,并且在位图里面标明 5 已经被占用了。如果TsFree(5),就在位图里面标明5没有人用。

TlsGetValue(index) 就是 GetCurrentTeb()->TlsSlots[index]; (你Debug一下你那个

int i = num;
004017B8 mov eax,[__tls_index (0047b040)]
004017BD mov ecx,dword ptr fs:[2Ch]
004017C4 mov edx,dword ptr [ecx+eax*4]

fs:0是TEB的便宜,2C显然是TlsSlots的位置,可以看出这段儿程序和上面是对应的,我就是刚才看到这个意识到我说错了。

64个Slot都用空了以后,会另外在Heap上分配另外空间,所以Index的数目不局限于64。
jsjwql 2007-06-27
  • 打赏
  • 举报
回复
有点头晕, 我的理解:
1.在进程可以动态申请多个index,在线程可以动态申请多个index,但是这些index都是属于进程所有,不归线程所有,通过TlsAlloc()获得。

2.然后动态内存应该在线程里面进行分配,分配完通过TlsSetValue(gdwTlsIndex, lpvBuffer);
如果在一个线程里面需要分配多个动态内存的,是不是每个动态内存需要对应一个不同的Index

3.通过lpvBuffer = TlsGetValue(gdwTlsIndex);获得动态内存的值。
一个Index就是对应一个表儿的指针?它对应一个什么样的表?每个线程都有自己的slot表,这表里的每个格子里方的是放到是什么?
feimingbiao 2007-06-27
  • 打赏
  • 举报
回复
哦,没看到你上面的问题。

一个线程里面可以有很多Index,可以通过TsAlloc来获得。一个Index就是对应一个表儿的指针。这个表儿里面每个线程自己有个格子。每一个Index都是进程中大家共享的(这个名字Index特别容易让人迷糊)。格子的位置存在每个线程的Teb里面,定义为:

TlsSlots[64];

所以每个进程最多只能有64个表。(后来好像加了,忘了,惭愧)多了每个线程就记不住格子位置了。

当你调用TlsGetValue的时候,通过表的索引和格子的索引就能找到你的变量值。

__declspec(thread)我从来没用过,其实就是用了个隐含的表儿索引 __tls_index.__tls_index和其他Index一样,自然也是线程共享了。

不知道说明白了没有,我自己都快晕了。:)
ReverseEngineering 2007-06-27
  • 打赏
  • 举报
回复
学习!!!!
feimingbiao 2007-06-27
  • 打赏
  • 举报
回复
唉,学艺不精啊,还得回头翻书,这是MSDN说的:

If a DLL declares any nonlocal data or object as __declspec( thread ), it can cause a protection fault if dynamically loaded. After the DLL is loaded withLoadLibrary, it causes system failure whenever the code references the nonlocal __declspec( thread ) data. Because the global variable space for a thread is allocated at run time, the size of this space is based on a calculation of the requirements of the application plus the requirements of all of the DLLs that are statically linked. When you use LoadLibrary, there is no way to extend this space to allow for the thread local variables declared with __declspec( thread ). Use the TLS APIs, such asTlsAlloc, in your DLL to allocate TLS if the DLL might be loaded with LoadLibrary.

意思就是说全局变量的空间在建立你的线程的时候就分配好了,不能再扩展了。所以如果你用LoadLibrary调入一个带TLS的DLL,OS无法为这些新的全局变量另外分配空间,也就无法初始化了。所以是0x00000000地址(没有空间),你一用就AV了。

COM CoCreateInstance最后要LoadLibrary你的DLL,所以也会出现同样问题。所以像MSDN所说,如果你的DLL是从LoadLibrary装进来的,用TLS API,而不是编译器带的这个_declspec(thread)
jsjwql 2007-06-27
  • 打赏
  • 举报
回复
是的,我做了两种测试,一种是自动引用的,一种是loadlibrary+GetProcAddress的方式。都成功了。
但是在MSDN里面看到这样的描述:
If a DLL declares any nonlocal data or object as __declspec( thread ), it can cause a protection fault if dynamically loaded. After the DLL is loaded with LoadLibrary, it causes system failure whenever the code references the nonlocal __declspec( thread ) data. Because the global variable space for a thread is allocated at run time, the size of this space is based on a calculation of the requirements of the application plus the requirements of all the DLLs that are statically linked. When you use LoadLibrary, there is no way to extend this space to allow for the thread local variables declared with __declspec( thread ). Use the TLS APIs, such as TlsAlloc, in your DLL to allocate TLS if the DLL might be loaded with LoadLibrary.
jsjwql 2007-06-27
  • 打赏
  • 举报
回复
feimingbiao(), 你应该对动态分配比较了解,我有个疑惑,是一个进程对应一个__tls_index,还是进程中的每个线程对应一个__tls_index。 它做到线程局部存储的机理是什么?
feimingbiao 2007-06-27
  • 打赏
  • 举报
回复
你说普通的dll测试没有问题,你的DLL是自动加载的还是你的主程序用了LoadLibrary?自动加载肯定没有问题,LoadLibrary也没有问题吗?
jsjwql 2007-06-27
  • 打赏
  • 举报
回复
你说的对,我调试的时候看不到__declspec(thread) static int num = 321; num的值,好像是没有赋值。
客户段也是vc的程序。
COM 其实就是个LoadLibrary? 但是我在普通的dll里面测试的时候没有这个问题,一切正常。
feimingbiao 2007-06-27
  • 打赏
  • 举报
回复
你的客户端也是VC的程序吗?

这种静态的TLS定义有些危险"__declspec(thread)"。就是LoadLibrary有可能不初始化__tls_index,这样你就出现0x000000了(因为你的__tls_index没有初始化)。 COM 其实就是个LoadLibrary,我估计很可能没有叫LdrInitializeTls。(不敢100%肯定,我得查一下)。但是静态使用在Kernel初始化的时候100%会叫这个。

你随便写个程序,直接LoadLibrary=》Test,看有没有类似问题。 还是尽量少用TLS了,如果一定要用还是动态分配安全些。
jsjwql 2007-06-27
  • 打赏
  • 举报
回复
从这里的讨论看,好像是不支持,不知道有没有什么好的解决方法?
星羽 2007-06-27
  • 打赏
  • 举报
回复
看看这个看有帮助不

http://topic.csdn.net/t/20020402/00/615751.html

3,248

社区成员

发帖
与我相关
我的任务
社区描述
ATL,Active Template Library活动(动态)模板库,是一种微软程序库,支持利用C++语言编写ASP代码以及其它ActiveX程序。
社区管理员
  • ATL/ActiveX/COM社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧