C 在动态DLL中使用线程专用变量?
我正在寻找一些关于OpenMP的建议。我对threadprivate变量的使用感到困惑。该问题可在以下示例中显示:C 在动态DLL中使用线程专用变量?,c,multithreading,visual-studio-2010,openmp,C,Multithreading,Visual Studio 2010,Openmp,我正在寻找一些关于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;
-----------
// 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);
}
-----------
-----------
//下面的代码位于动态加载的DLL中。
/*全局变量*/
int*p;
#pragma omp threadprivate(p)
外部“C”uu declspec(dllexport)int MyFunc1(void)
{
int i;
#pragma-omp并行
对于(i=0;i
在这里,我希望每个线程都有一个全局线程独立变量的单独副本,该变量将在线程的所有函数中可见。变量将在线程中初始化和销毁
这里的“问题”是,包括全局变量“p”的定义在内的所有代码都位于动态加载的DLL中(通过LoadLibrary)
Microsoft说:“您不能在任何将通过LoadLibrary加载的DLL中使用threadprivate。这包括使用/DELAYLOAD(延迟加载导入)加载的DLL,后者也使用LoadLibrary。”因此,如果我没有弄错,上述代码是不正确的–threadprivate变量和动态加载的DLL不能混合使用
为了验证这一点,我创建了一个测试项目,动态加载一个DLL add,并使用threadprivate并行运行一个函数,如上所述。一切都很好
嗯。。。现在我很困惑,因为那个项目本不应该起作用
我真的可以在dinamic DLL中使用线程私有变量吗?或者有什么诀窍吗
谢谢,
Alex我没有尝试OMP,但我有很好的OMP工作经验
DWORD WINAPI TlsAlloc(void);
以及其他TlsXxx
功能。我非常确定threadprivate(p)
是最终用于此函数的包装器。此函数在exe、静态和动态加载的dll等环境中都能完美工作
在一个案例中,我在这个过程中同时看到了大约800个TLS索引。每个线程(大约200个线程)的线程本地存储中都有这个数量的对象。NT正在堆中分配缓冲区来存储此数据。这一切都很好
在撰写MSDN文章时,可能存在一些问题,但很可能现在已经解决了
MS OpenMP实现中的My 2 cent.
threadprivate
被转换为\u declspec(thread)
,它将声明的变量放入静态TLS(线程本地存储)中。当程序启动时,TLS的大小通过考虑可执行文件所需的TLS大小以及所有其他隐式加载DLL的TLS要求来确定。当您使用LoadLibrary
动态加载另一个DLL或使用freebrary
卸载它时,系统必须检查所有正在运行的线程,并相应地放大或压缩它们的TLS存储。根据:
此过程对于操作系统来说太多,无法管理,这可能会在动态加载DLL或代码引用数据时导致异常
访问此类变量被视为未定义的行为。它适用于你的情况,但并不意味着它在任何地方、任何时候都适用。您可以了解它在Windows XP/2003和更早版本上最有可能失败的原因。根据同一来源,隐式TLS处理在Windows Vista中被重写,因此OpenMPthreadprivate
和\u 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);
}
有关更多详细信息,请参见(带有错误检查)。Hristo和Kirill
谢谢你的回复
看起来微软已经修复了动态DLL中TLS的问题(至少在我使用的Windows7中)。也许他们只是没有更新文档。我祈求好运
我创建了一个测试项目,使用TLS通过_udeclspec(thread)pragmas(正如赫里斯托·伊利耶夫在另一篇文章中向我建议的那样)测试了这个想法。该项目使用了大量的TLS变量(只是为了测试),而且一切都很好。然后我在工作项目中移动了代码。这是一个加载许多动态DLL的大型项目(约100万行代码)。这一切在那里也起了作用
我仍然坚持在更糟糕的情况下使用TlsAlloc、TlsSetValue、TlsGetValue函数的想法。但是它并不适合我,因为我担心这些函数会给我的数字运算代码增加一个明显的间接影响。我的库只需要几个TLS变量,但这些变量在代码中被广泛使用,包括位于堆栈深处的低级函数
干杯
亚历克斯·亚姆奇科夫(Alex Yamchikov)它的效果并不好。你以为它会失败,但它没有失败。不做你期望的事情永远不应该被描述为工作得很好。你违反了规则,它没有达到你的预期。这是你首先应该想到的。显然它仍然会在Windows XP上引起问题-请参阅。我在提到的文章中没有看到与“如果没有足够的资源,函数可能会失败”不同的内容。它们没有指出任何具体问题。而且他们没有提到任何最新的windows版本。答案中的代码示例仍然将数据存储在同一堆中。您对
malloc
的调用也很容易以同样的方式失败。您可以在并行区域的开始处获取指向TLS区域的指针,并将其沿调用堆栈传递给使用线程专用变量的函数。隐式TLS访问也以64位模式下的-4MOV
指令的价格提供,这些指令实现了TlsGetValue(\u TLS\u index)
的精简内联版本。
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);
}