最近公司项目用到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");
}
相关推荐
本项目重点在于如何在JNI中利用多线程调用Java代码,这在处理大量数据或者并发任务时非常有用。 首先,理解JNI的基本概念至关重要。JNI提供了一种方式,让本地(非Java)代码可以访问和调用Java类、方法和字段。这...
Java JNI例子-创建DLL、项目导入DLL、IDEA配置JNI、JNI调用DLL(该DLL同时依赖第三方DLL)
完整的实现java跨平台调用C程序源码,包含JAVA源码和C源码以及编译后的demo dll。将dll放到jdk bin目录下,java 项目可以...该demo处理了多线程调用c,全局变量干扰问题。源码全是自己写的,整个过程以及结果完整验证
- Java类中,`native`方法声明和`System.loadLibrary`加载本地库,本地库中的C/C++代码实现线程创建并在完成任务后调用Java的回调方法。 6. **应用实例**: - 这种技术常用于异步任务,如网络请求、文件读写等。...
总的来说,理解和实现"C++ JNI多线程回调java"涉及到对JNI接口的深入理解,对多线程编程的掌握,以及对Java并发模型的认识。这是一个高级的跨语言编程话题,需要开发者具备扎实的C++和Java基础。在实际项目中,这样...
### 使用JNA替代JNI调用DLL,并解决内存溢出问题 #### 问题背景 在项目的开发过程中,常常遇到需要处理二进制流数据并对其进行解析处理的情况。这种情况下,如果上层应用平台采用的是Java开发,而底层算法或数据...
- 跨语言调用可能涉及线程安全问题,需要确保在多线程环境下正确同步。 - 针对不同的操作系统,可能需要调整DLL的加载和调用方式,因为Windows使用`LoadLibrary`,而在Unix-like系统中通常使用`dlopen`。 本项目...
Java 通过 JNI 调用 C++ 的 DLL 文件 JNI(Java Native Interface)是一种允许 Java 代码和本地应用程序交互的技术。通过 JNI,Java 应用程序可以调用本地库,反之亦然。本文将详细介绍如何使用 JNI 将 Java 应用...
在C/C++中,我们可以利用互斥量(Mutex)、条件变量(Condition Variable)、信号量(Semaphore)或者临界区(Critical Section)等来实现线程同步。其中,互斥量是最常见的一种方式,可以保证同一时间只有一个线程...
在Java中,通过JNI接口,开发者可以定义native方法,并在C或C++代码中实现这些方法,以便调用DLL中的函数。这涉及到以下步骤: 1. 在Java类中声明native方法。 2. 使用javah工具生成C/C++头文件,该文件定义了JNI...
Java通过JNI调用DLL动态库,亲测试编写
在某些场景下,比如调用操作系统特定的功能或者利用已有的C/C++库,我们需要使用JNI来实现Java与本地代码(如DLL动态链接库)的交互。本教程将详细介绍如何通过JNI在Java中调用DLL的完整步骤。 1. **创建Java类和...
Java使用JNI调用DLL来实现系统热键屏蔽与任务栏隐藏是一种跨平台编程技术的应用,主要涉及Java的本地接口(JNI)和Windows API。本文将深入解析这一技术的关键点,并提供相关的知识背景。 首先,JNI(Java Native ...
在这个"JNI DEMO"中,我们将探讨如何使用JNI来调用C/C++编译生成的DLL(动态链接库)文件。 首先,我们需要了解JNI的基本工作原理。JNI为Java程序提供了一种机制,使得Java代码可以声明native方法,这些方法在Java...
GMSSL是一种专为我国国密算法提供支持的SSL/TLS协议实现,它包含了SM2、SM3和SM4等加密算法。在这个“GMSSL的java调用(JNI库和调用实例).zip”压缩包中,我们主要探讨如何在Java环境中通过JNI(Java Native ...
在本案例中,我们讨论的是如何使用Java JNI调用一个名为"AlarmTTS"的VC(Visual C++)动态链接库(DLL)并进行调试。 首先,我们需要了解Java部分的代码。`CallAlarmTTSDll`类是Java程序的核心,它定义了三个本地...
- 配置编译选项:在Visual Studio中,设置生成DLL的配置,包括添加`/MD`(多线程DLL)编译器选项。 - 生成DLL:使用`cl`命令行编译器,将源文件编译成DLL和对应的.lib文件。 3. **在Linux上生成SO文件** - 使用...
通过本文,我们可以了解 Java 和 C++ 之间的互相调用实例的实现过程,包括 C++ DLL 文件的创建和使用,以及 Java 代码中使用 JNI 技术调用 C++ DLL 文件的实现细节。这种技术可以应用于需要 Java 和 C++ 之间互相...
总结来说,这个实例展示了如何使用JNI调用第三方DLL的基本步骤:在Java中声明native方法,生成JNI头文件,实现C/C++代码并调用DLL函数,最后编译生成本地库。虽然这是一个简单的例子,但理解这些基本概念对于深入...
在这种情况下,"springboot+jna/jni调用动态so/dll库"是一个重要的主题,它涉及到Spring Boot应用如何利用Java Native Interface (JNI) 和 Java Native Access (JNA) 这两种技术来调用操作系统级别的动态链接库(.so...