我正在寻找有关OpenMP的一些建议。我对使用threadprivate变量感到困惑。问题可以在以下示例中显示:
-----------
// The code below is located the dynamically loaded DLL.
/* Global variable. */
int *p;
#pragma omp threadprivate(p)
extern "C" __declspec(dllexport) int MyFunc1(void)
{
int i;
#pragma omp parallel for
for (i = 0; i < n; i++) {
MyFunc2(i);
}
return TRUE;
}
void MyFunc2(void)
{
p = malloc(sizeof(int));
*p = 0;
printf(“value = %d”,*p);
free(p);
}
-----------
这里我希望每个线程都有一个独立的全局线程无关变量的副本,该变量将在线程的所有函数中可见。变量将在线程中初始化和销毁。
这里的“问题”是包含全局变量“p”定义的所有代码都位于动态加载的DLL中(通过LoadLibrary)。
Microsoft说http://msdn.microsoft.com/en-us/library/2z1788dd.aspx:“您不能在任何将通过LoadLibrary加载的DLL中使用threadprivate。这包括使用/ DELAYLOAD(延迟加载导入)加载的DLL,它也使用LoadLibrary。“因此,如果我说得对,上面的代码不正确 - threadprivate变量和动态加载的DLL不会混合。
为了验证这一点,我创建了一个动态加载DLL的测试项目,使用threadprivate并行运行一个函数,如上所述。这一切都很好!
嗯......现在我感到很困惑,因为该项目不应该起作用。
我真的可以在dinamic DLL中使用线程私有变量,还是有诀窍呢?
谢谢,
亚历
答案 0 :(得分:1)
threadprivate
被转换为__declspec(thread)
,它将声明的变量放在静态TLS(线程局部存储)中。启动程序时,通过考虑可执行文件所需的TLS大小以及所有其他隐式加载的DLL的TLS要求来确定TLS的大小。当您使用LoadLibrary
动态加载另一个DLL或使用FreeLibrary
卸载它时,系统必须检查所有正在运行的线程并相应地放大或压缩它们的TLS存储。根据{{3}}:
此过程对于操作系统来说太多了,这可能会在动态加载DLL或代码引用数据时导致异常。
访问此类变量被视为未定义的行为。它适用于您的情况,但并不意味着它可以随处运行。 KB118816您可以在Windows XP / 2003及更早版本的Windows版本中阅读其最有可能失败的原因。根据相同的来源,隐式TLS处理在Windows Vista中被重写,因此OpenMP threadprivate
和__declspec(thread)
应该在运行时加载的DLL中正常运行。建议的解决方案是使用TlsAlloc
代替。
DWORD dwTlsIdx;
extern "C" __declspec(dllexport) int MyFunc1(void)
{
int i;
#pragma omp parallel for
for (i = 0; i < n; i++) {
MyFunc2(i);
}
return TRUE;
}
void MyFunc2(void)
{
int **pp = (int **)TlsGetValue(dwTlsIdx);
*pp = malloc(sizeof(int));
**pp = 0;
printf(“value = %d”,**pp);
free(*pp);
}
dwTlsIdx
应在DllMain
的流程附加时初始化,并调用TlsAlloc
。应该在线程附加上分配足够的内存来保存int *
,并且应该将其地址设置为dwTlsIdx
TLS索引的值。或者您可以在第一次拨打MyFunc2
时执行此操作:
void MyFunc2(void)
{
int **pp = (int **)TlsGetValue(dwTlsIdx);
if (pp == NULL)
{
pp = malloc(sizeof(int *));
TlsSetValue(dwTlsIdx, pp);
}
*pp = malloc(sizeof(int));
**pp = 0;
printf(“value = %d”,**pp);
free(*pp);
}
有关详细信息,请参阅Here(错误检查)。
答案 1 :(得分:0)
我没有尝试过OMP,但我有很好的使用经验
DWORD WINAPI TlsAlloc(void);
和其他TlsXxx
函数。我很确定threadprivate(p)
是一个最终来到这个函数的包装器。此函数在exe,静态和动态加载的dll等中完美运行。
在一个案例中,我在我的流程中同时看到了大约800个TLS索引。每个线程(大约200个线程)在其线程本地存储中具有此数量的对象。 NT在堆中分配缓冲区来处理这些数据。这一切都很好。
可能是在撰写MSDN文章时,存在一些问题,但很可能现在已经修复了。
我的2分。
答案 2 :(得分:0)
Hristo和Kirill,
感谢您的回复。
似乎Microsoft已在动态DLL中解决了TLS问题(至少在我使用的Windows 7中)。也许他们只是没有更新文档。我保持手指交叉。
我创建了一个测试项目,我通过__declspec(thread)编译指示使用TLS测试了这个想法(正如Hristo Iliev在另一篇文章中向我建议的那样)。该项目使用了大量的TLS变量(仅仅是为了测试)而且一切正常。然后我移动了工作项目中的代码。这是一个大型项目(约100万行代码),可以加载许多动态DLL。这一切都在那里工作。
我仍然坚持使用TlsAlloc,TlsSetValue,TlsGetValue函数来处理更糟糕的情况。但它并不适合我,因为我担心这些功能会增加对我所拥有的数字运算代码的重大监听。我的库只需要一些TLS变量,但这些变量通过代码广泛使用,包括位于堆栈深处的低级函数。
干杯,
Alex Yamchikov