C 语言DLL跨平台调用时,支持多线程TLS方法的使用
来源:互联网 发布:金庸x 知乎 编辑:程序博客网 时间:2024/06/14 04:18
在java多线程调用C语言生成的dll动态库时,遇到问题,多个线程之间共享一份全局和静态变量,导致结果异常甚至运行错误。经过研究发现需要使用TLS技术来进行多线程本地存贮,为每一个线程开辟一块空间来存储C语言生成的dll动态库中需要保存的全局变量,这样来保证多线程调用dll中的方法时,dll中的全局变量相互独立,不受其他线程干扰。
看了一个博客来介绍TLS的使用: http://blog.csdn.net/bingge1022/article/details/73274229
要想在C语言跨平台调用时,能够契合TLS方法,需要在C语言中指定需要在跨语言调用时需要使用TLS技术的变量。主要操作分为4种:分配空间、取数、存数、释放空间。
1:分配空间,上面那篇博客中提到MSDN主页上介绍TLS的内容,其中分配TLS空间的函数为DllSet(),分配空间使用命令:DllSet(DLL_PROCESS_ATTACH)
但该函数体内包含具体的在TLS.cpp中声明的全局变量
int threadId;bool DllSet(int fdwReason){ ... if ((threadId = TlsAlloc()) == TLS_OUT_OF_INDEXES); ... fIgnore = TlsSetValue(threadId, lpvData); ... lpvData = TlsGetValue(threadId);...}
对此我十分的费解,一个功能函数怎么会对某个具体变量(该变量是全局变量,而非 输入参数)进行一些操作呢?由于对TLS机制不是很了解,只能自以为对的解释为这样是一个线程中进行某一个变量的操作,就可以完成该线程全部全局变量的TLS分配。结果却说明根本不是这样,后面会讲到。
2:取值,GetData(int *piv, int intTlsVar)//将intTlsVar的值赋给piv,当通过DllSet对某个全局变量进行TLS空间分配后,就不能像在C程序中那样简单使用变量了,因为该变量被分配进了TLS空间(为方便说明,本文称之为TLS变量),所以每次获取TLS变量的值时都需要通过GetData()函数来获取TLS变量的值,且无法直接获取TLS变量的值,只能新声明一个与TLS变量同类型的变量,调用GetData()将TLS变量的值取出来,例如,变量int a为全局变量,已经调用DLLSet进行TLS空间分配:使用时应参照以下方式,
int temp_a = 0; temp_a = GetData(temp_a, a);
…
用temp_a 进行运算。。。。。。
3: 赋值
…
在return 之前,利用temp_a 的值赋给a:
StoreData(temp_a, a);
4:释放TLS空间:
当TLS变量不再使用时,将TLS变量所占的空间释放掉。
DllSet(DLL_PROCESS_DETACH)
多线程测试单TLS变量,没有问题,通过。
单线程测试多TLS变量,这就触碰到了一开始觉得很疑惑的地方,DllSet()中对具体的某个变量名进行操作,那结果是只对该变量进行了空间开辟还是说DllSet.cpp中的变量相当于形参,对某个变量开辟TLS空间就是对该线程开辟出了空间,在该线程中的其余变量自动分配TLS空间?
结果就是,只对DllSet()中操作的那个变量开辟了空间,更准确的说,只是因为DllSet()对那个具体的int变量的操作,为该线程分配了一个int 大小的空间,多个变量共用这一个变量的空间,得到这个结论也是做了很多的测试,
int a;int b;int c;//注意,如果a、b、c是你想要的TLS变量,不要在定义时进行初始化。int main(){ Init(); int a_temp ; int b_temp ; int c_temp ;for (int i = 0; i < 3; i++){ GetData(a_temp,a); GetData(b_temp,b); GetData(c_temp,c); printf( "%d\t%d\t%d\n",a_temp,b_temp,c_temp); a_temp = 2; b_temp = 1; c_temp = 0; StoreData(a_temp ,a); StoreData(b_temp ,b); StoreData(c_temp ,c);}return 0;}int Init(){ StoreData(0,a);//a 初始值设为0,之前简单的int a = 0,现在需要两部才能完成。 StoreData(0,b); StoreData(0,c); return 0;}
得到的结果是:
0 0 0
0 0 0
0 0 0
0 0 0
开始怀疑初始化的问题(因为初始化全为0),单步调试到库函数也未果,初始化也没有在循环体内,为何StoreData的时候都是正确的,当再次循环GetData()时却全变成0?
茫茫的测试,茫茫的茫然,将C的赋值由 StoreData(c_temp ,c); 改为 StoreData(3,c);
那么得到的结构是:
3 3 3
3 3 3
3 3 3
改为StoreData(55,c);
结果为:
55 55 55
55 55 55
55 55 55
恍然大悟:因为StoreData(3,c);是在最后执行的,循环调用时取到的值都是最后一次调用StoreData()的结果。也就是DllSet()只分配了一个int 大小的空间作为TLS变量,无论程序中声明多少个变量,都是公用这一个空间,这就解释了,对全体变量取值,取到的都是最后一次StoreData的结果。
可是DllSet是对某个具体变量的操作,难不成要每声明一个变量就要赋值一份DllSetVarName(),然后用函数名区分,一个变量声明对应一个DllSetVarName?
那就太笨了,程序很冗余,也不优雅。那就想想办法!
能不能将变量名作为输入参数,那么只需要一个函数,就可以完成所有变量的TLS空间分配?按照这个思路,进行了实验,将原来的
int threadId;bool DllSet(int fdwReason){...}
改为
bool DllSet(int fdwReason,int &threadId){...}
结果是:对于所有的int型变量,在Init()时,调用DllSet(int fdwReason,int &threadId)对其进行TLS空间分配,这样就一切正常了!经测试,各个变量之间独立不干涉。 当然对于short型和其他型的变量,对DllSet(int fdwReason,int &threadId)进行调整就可以啦。
- C 语言DLL跨平台调用时,支持多线程TLS方法的使用
- 关于JNI的使用(实战linux平台下java调用本地c语言方法)
- Java调用C语言DLL文件方法
- bcb平台的C++dll的静态调用法和动态调用法的方法
- bcb平台的C++dll的静态调用法和动态调用法的方法
- JNI多线程调用DLL全局变量处理,TLS实现
- JAVA调用C语言的dll库(使用Dev C++创建Dll)
- 调用C语言编写的DLL文件
- vb下调用C++dll的方法
- C#调用C++DLL的方法
- C#动态调用c++DLL的方法
- c#dll调用的一些方法
- C#动态调用c++DLL的方法
- C语言支持的两种函数指针的调用方法
- Boost asio学习笔记之一—— 使用strand支持多线程调用service_io的方法
- 使用 JNI 调用 c 的 DLL
- 易语言调用c++dll
- java使用JNA调用dll的方法
- linux内核部件分析之——waitqueue与线程的阻塞
- 【Java多线程】ThreadLocal实现原理
- Unable to locate package错误解决办法以及jdk的切换
- Huffman编码——文件压缩项目
- C语言:生产者-消费者问题、读者-写者问题
- C 语言DLL跨平台调用时,支持多线程TLS方法的使用
- 关于Spring返回json的问题
- java 树的应用
- [Unity] A* pathfinding project integrated with influence map
- 【JAVA】Spring 自动注入类注释详解
- TCP/UDP通信协议基础全集(区别,三次握手四次挥手)
- 联网请求操作okhttputils
- Python第三方库h5py——读取mat文件并显示值
- modern c++ design