最近公司项目用到C/C++的跨平台调用,因为调用方是JAVA,所以调用方式选择了JNI,但是在实现过程中遇到了颇多问题。今天就说一说其中一个,DLL多线程全局变量互相干扰的问题。
JAVA的业务需要在调用过程中采用多线程的方式,因为C实现算法中用到了很多全局静态变量,JNI在调用的时候就不可避免的出现各个线程间的全局变量互相干扰的问题。然后各种查找解决方案。
最初是想在不改DLL的前提下解决,尝试的是通过java掉命令的方式,在多个进程中调用dll,问题肯定是可以解决的,但是综合考虑系统资源开销太大。PASS
最后决定修改DLL,敲定的解决方案是使用TLS方式存储用到的全局变量。各种查询,发现了C/C++解决全局变量多线程调用互相干扰的问题很简单的方式,就是在用到的全局变量前都加上__declspec(thread)来修饰就可以了(例如:__declspec(thread) int index;)。看起来确实很简单,开始修改DLL并调用调试,但是结果却不是跟预想中的一样!!!继续谷哥、度娘,http://blog.csdn.net/pgmsoul/article/details/8580415,看到了这位仁兄的这篇文章,豁然开朗。原来__declspec(thread)这种方式在动态连接库中调用是不行的,需要自己去实现TLS存储。好吧,还是去找权威吧https://msdn.microsoft.com/en-us/library/ms686997(v=vs.85).aspx,这里写的很详细了。根据微软说明,简单封装了一下需要用到的函数,调试通过,问题解决了。
下载调试过程中的DLL源码,请移步至 http://download.csdn.net/detail/bingge1022/9870979
Tls.cpp源码
#include "Tls.h"
int threadId;
bool DllSet(int fdwReason)
{
LPVOID lpvData;
BOOL fIgnore;
switch (fdwReason)
{
// The DLL is loading due to process
// initialization or a call to LoadLibrary.
case DLL_PROCESS_ATTACH:
// Allocate a TLS index.
if ((threadId = TlsAlloc()) == TLS_OUT_OF_INDEXES)
return FALSE;
// No break: Initialize the index for first thread.
// The attached process creates a new thread.
case DLL_THREAD_ATTACH:
// Initialize the TLS index for this thread.
lpvData = (LPVOID) LocalAlloc(LPTR, 256);
if (lpvData != NULL)
fIgnore = TlsSetValue(threadId, lpvData);
break;
// The thread of the attached process terminates.
case DLL_THREAD_DETACH:
// Release the allocated memory for this thread.
lpvData = TlsGetValue(threadId);
if (lpvData != NULL)
LocalFree((HLOCAL) lpvData);
break;
// DLL unload due to process termination or FreeLibrary.
case DLL_PROCESS_DETACH:
// Release the allocated memory for this thread.
lpvData = TlsGetValue(threadId);
if (lpvData != NULL)
LocalFree((HLOCAL) lpvData);
// Release the TLS index.
TlsFree(threadId);
break;
default:
break;
}
return TRUE;
}
bool StoreDataInt(int iv, int intTlsVar)
{
LPVOID lpvData;
int * pData; // The stored memory pointer
lpvData = TlsGetValue(intTlsVar);
if (lpvData == NULL)
{
lpvData = (LPVOID) LocalAlloc(LPTR, 256);
if (lpvData == NULL)
return FALSE;
if (!TlsSetValue(intTlsVar, lpvData))
return FALSE;
}
pData = (int *) lpvData;
// Cast to my data type.
// In this example, it is only a pointer to a int
// but it can be a structure pointer to contain more complicated data.
(*pData) = iv;
return TRUE;
}
bool GetDataI(int *piv, int intTlsVar)
{
LPVOID lpvData;
int * pData; // The stored memory pointer
lpvData = TlsGetValue(intTlsVar);
if (lpvData == NULL)
return FALSE;
pData = (int *) lpvData;
(*piv) = (*pData);
return TRUE;
}
int GetDataInt(int intTlsVar)
{
int ivOut;
if(GetDataI(&ivOut, intTlsVar)){
return ivOut;
}
return -1;
}
bool StoreDataChar(char cv, char charTlsVar)
{
LPVOID lpvData;
int * pData; // The stored memory pointer
lpvData = TlsGetValue(charTlsVar);
if (lpvData == NULL)
{
lpvData = (LPVOID) LocalAlloc(LPTR, 256);
if (lpvData == NULL)
return FALSE;
if (!TlsSetValue(charTlsVar, lpvData))
return FALSE;
}
pData = (int *) lpvData;
// Cast to my data type.
// In this example, it is only a pointer to a int
// but it can be a structure pointer to contain more complicated data.
(*pData) = cv;
return TRUE;
}
bool GetDataC(char *pcv, char charTlsVar)
{
LPVOID lpvData;
int * pData; // The stored memory pointer
lpvData = TlsGetValue(charTlsVar);
if (lpvData == NULL)
return FALSE;
pData = (int *) lpvData;
(*pcv) = (*pData);
return TRUE;
}
int GetDataChar(char charTlsVar)
{
int cvOut;
if(GetDataI(&cvOut, charTlsVar)){
return cvOut;
}
return -1;
}
取值\赋值调用关键代码
int _intTlsVar = GetDataInt(intTlsVar);
_intTlsVar = _intTlsVar+1;
if(!StoreDataInt(_intTlsVar, intTlsVar)){
printf("%s","StoreData error");
}