java JNI 8 C++:线程附加和分离和异步回调
如何从std::thread异步调用Java方法
让我们假设这是一个imbotsdk,因为它的逻辑基本上是一个imbotsdk
最重要的是:如何异步调用java方法和回调本机
底部有逻辑流程,可能有帮助
例如:
收到一条“备份”消息,然后用MsgA调用java插件,插件处理此事件需要10秒,并调用5次本机方法以获取所需信息
同时,接收消息B“echo”,处理只需10毫秒,并通过调用本机方法发送消息
因此,MsgB在MsgA之后接收,但在MsgA之前完成
如果使用纯C++语言或java语言,那么,这将是多么容易实现。但在这里我发现了一个令人头痛的问题:JNI线程连接※第一个问题:有线JNI连接
我已经阅读了doc find的答案,它们都不起作用,我的情况与每个人都不同
我使用Zulu JDK8(Zulu840.53-CA-FX-JDK80.265-WiNx64)和MIW64 C++,用于演示:
public class ThreadTest {
private static int count = 0;
private static final Random random = new Random();
public static int count() {
try {
Thread.sleep(random.nextInt(2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
return count++;
}
}
下面是C中的worker函数++
void worker(JNIEnv* localEnv) {
jclass clazz = localEnv->FindClass("ThreadTest");
jmethodID method = localEnv->GetStaticMethodID(clazz, "count", "()I");
jchar result = localEnv->CallStaticCharMethod(clazz, method);
int tid = std::hash<std::thread::id>{}(std::this_thread::get_id());
printf("[Worker Done] %d =>> %d\n", tid, result);
}
如果没有附件,我们将得到,这是预期的:
worker(env);
// Here the first call from main thread, Working find;
// [Worker Done] -1444639049 =>> 0
jvm->DetachCurrentThread();
std::thread t1(worker, env);
t1.join();
// Process crashed because not attach jni
// Process finished with exit code -1073741819 (0xC0000005)
并为t1添加tWorker函数:
void tWorker (JavaVM* gJvm) {
int tid = std::hash<std::thread::id>{}(std::this_thread::get_id());
printf("[Thread Run] %d\n", tid);
JavaVMAttachArgs* args;
args->version = JNI_VERSION_1_8;
args->name = nullptr;
args->group = nullptr;
JNIEnv* lEnv;
printf("[Attach for] %d\n", tid);
int attachResult = gJvm->AttachCurrentThread(reinterpret_cast<void**>(lEnv), &args);
printf("[Attach Done] %d =>> %d\n", tid, attachResult);
delete args;
worker(lEnv);
gJvm->DetachCurrentThread();
}
我明白了:
[Worker Done] -1444639049 =>> 0
[Thread Run] 1709724944
Process finished with exit code -1073741819 (0xC0000005)
一些答案说你应该使用GetEnv
:
void tWorker02(JavaVM* gJvm, JNIEnv* gEnv) {
int tid = std::hash<std::thread::id>{}(std::this_thread::get_id());
printf("[Thread Run] %d\n", tid);
JavaVMAttachArgs* args;
args->version = JNI_VERSION_1_8;
args->name = nullptr;
args->group = nullptr;
JNIEnv* lEnv;
printf("[GetEnv for] %d\n", tid);
int getEnvResult = gJvm->GetEnv(reinterpret_cast<void**>(lEnv), JNI_VERSION_1_8);
printf("[GetEnv Done] %d =>> %d\n", tid, getEnvResult);
printf("[Attach for] %d\n", tid);
int attachResult = gJvm->AttachCurrentThread(reinterpret_cast<void**>(lEnv), &args);
printf("[Attach Done] %d =>> %d\n", tid, attachResult);
delete args;
worker(gEnv);
gJvm->DetachCurrentThread();
}
得到了同样的结果:
[Worker Done] -1444639049 =>> 0
[Thread Run] 1709724944
Process finished with exit code -1073741819 (0xC0000005)
对于我找到的更多帖子,请将本地替换为全局(这对于逻辑和文档来说没有任何意义,但问题已经解决)
//JNIEnv* lEnv;
printf("[GetEnv for] %d\n", tid);
int getEnvResult = gJvm->GetEnv(reinterpret_cast<void**>(gEnv), JNI_VERSION_1_8);
printf("[GetEnv Done] %d =>> %d\n", tid, getEnvResult);
printf("[Attach for] %d\n", tid);
int attachResult = gJvm->AttachCurrentThread(reinterpret_cast<void**>(gEnv), &args);
printf("[Attach Done] %d =>> %d\n", tid, attachResult);
这是usless,即使我尝试所有16个组合,对我来说也不起作用
[Worker Done] -1444639049 =>> 0
[Thread Run] 1709724944
Process finished with exit code -1073741819 (0xC0000005)
问题一:那里发生了什么
※第二个问题:如何实现:
更新1:
问题1已解决
void tWorker02(JavaVM* gJvm, JNIEnv* gEnv) {
int tid = std::hash<std::thread::id>{}(std::this_thread::get_id());
printf("[Thread Run] %d\n", tid);
auto* args = new JavaVMAttachArgs{};
args->version = JNI_VERSION_1_8;
args->name = nullptr;
args->group = nullptr;
JNIEnv* lEnv;
printf("[GetEnv for] %d\n", tid);
int getEnvResult = gJvm->GetEnv(reinterpret_cast<void**>(&args, JNI_VERSION_1_8);
printf("[GetEnv Done] %d =>> %d\n", tid, getEnvResult);
if (getEnvResult == JNI_EDETACHED) {
printf("[Attach for] %d\n", tid);
int attachResult = gJvm->AttachCurrentThread(reinterpret_cast<void**>(&lEnv), &args);
printf("[Attach Done] %d =>> %d\n", tid, attachResult);
}
delete args;
worker(gEnv);
gJvm->DetachCurrentThread();
}
没有强制转换将导致一个complie错误 error: invalid conversion from 'JNIEnv**' {aka 'JNIEnv_**'} to 'void**' [-fpermissive]
# 1 楼答案
看起来,你的问题不是在JVM的使用中,而是在C++代码中。看看这段代码:
请注意:
您的参数是指针,未初始化。它调用未定义的行为,最有可能崩溃
此外,您正在尝试在未初始化的情况下删除它:
我也不明白这段代码:
这里的重新解释是什么意思?根据函数的定义,需要指向指针的指针,而不是强制转换:
好的,您可以强制转换它,但是您应该在这里传递指向指针的指针,所以强制转换指向指针
static_cast<void**>(&lEnv)
的指针,但它可能不是必需的