最近公司項目用到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"); }