jni8c++:线程连接和分离以及异步回调

piztneat  于 2021-07-08  发布在  Java
关注(0)|答案(1)|浏览(284)

如何从std::thread异步调用java方法?
假设这是一个imbotsdk,因为它基本上是一个imbotsdk。
最重要的是:如何异步调用java方法和回调本机。
底部有逻辑流程,可能有用。
例如:
收到一条“backup”消息,然后用msga调用java插件,插件处理这个事件需要10秒,并根据需要调用5次本地方法。
同时,接收只需10ms处理的消息b“echo”,并通过invoke native方法发送消息。
所以,msgb在msga之后接收,但是在msga之前完成。
如果使用纯cjava或者别的什么东西,那就很容易实现了。但在这里我发现了一个令人头痛的问题:jni线程连接。
※第一个问题:有线jni连接
我读过医生的答案,他们都不工作,我的条件和每个人不同
我使用的是zulu jdk8(zulu8.48.0.53-ca-fx-jdk8.0.265-winïx64)和mingw64 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)

对于我找到的更多帖子,请将local替换为global(这对于逻辑和文档来说没有任何意义,但是在他们的问题上已经解决了)

//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);

这是无用功,即使我尝试所有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]

3qpi33ja

3qpi33ja1#

看起来您的问题不在jvm的使用上,而是在c++代码中。看看这段代码:

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;

注意这里:

JavaVMAttachArgs* args;
    args->version = JNI_VERSION_1_8;

args是指针,未初始化。它调用未定义的行为,最有可能崩溃。
另外,您正在尝试将其未初始化地删除:

delete args;

我也不明白这段代码:

JNIEnv* lEnv;
    ...
    int getEnvResult = gJvm->GetEnv(reinterpret_cast<void**>(lEnv), ...

重新诠释这里的意义是什么?根据函数的定义,需要指向指针的指针,而不是强制转换:

JNIEnv* lEnv;
    ...
    int getEnvResult = gJvm->GetEnv(&lEnv, ...

好的,您可以强制转换它,但是您应该在这里传递指针到指针,所以强制转换指针到指针 static_cast<void**>(&lEnv) ,但它可能不是必需的。

相关问题