最近公司项目用到C/C++的跨平台调用,由于调用方是JAVA,因此调用方式选择了JNI,可是在实现过程当中遇到了颇多问题。今天就说一说其中一个,DLL多线程全局变量互相干扰的问题。java
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源码this
#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; }
取值\赋值调用关键代码.net
int _intTlsVar = GetDataInt(intTlsVar); _intTlsVar = _intTlsVar+1; if(!StoreDataInt(_intTlsVar, intTlsVar)){ printf("%s","StoreData error"); }