jniデバッグ3(スレッドデバッグenv変数の問題)
2679 ワード
jniレイヤデバッグスレッドがフリーズした理由
一、ハングアップの原因:
jniレイヤのスレッド関数にenvを呼び出す関数を追加するとフリーズします
二、解決方法
第一に、私たちは理解しなければならない.
①(独立性)JNIEnvはスレッドに関する変数である.すなわちスレッドAにはJNIEnv変数があり、スレッドBにもJNIEnv変数があり、スレッドに関するため、AスレッドはBスレッドのJNIEnv構造体変数を使用できない.
では、どのようにして各スレッドJNIEnvの独立性を保証しますか?
1つのjavaオブジェクトは、JNIによってDLLのsend()関数を呼び出してサーバにメッセージを送信し、サーバメッセージが来るのを待たずにすぐに戻ると同時に、JNIインタフェースのポインタJNIEnv*env(仮想マシン環境ポインタ)、jobject objをDLLの変数に保存する.しばらくすると、DLLのメッセージ受信スレッドは、サーバからのメッセージを受信し、保存するenvとobjによって以前のjavaオブジェクトを呼び出す方法(JAVAコールバック方法に相当)を試みる、このメッセージを処理する際にプログラムが突然終了する(クラッシュ).
すなわち、フロントJAVAスレッドはメッセージを送信し、バックグラウンドスレッドはメッセージを処理し、2つの異なるスレッドに帰属し、同じJNIEnv変数を使用することはできない.ここでは、グローバルなJavaVM*ポインタを利用して現在のスレッドのJNIEnv*ポインタを得るメカニズムを利用することができ、C++の2つのスレッドでTLSを使用してローカルストレージを行うのと同様の原理を利用することができる.
具体的な方法:
グローバルなJavaVM変数を取得するには、次の手順に従います.
/* Our VM */JavaVM *g_vm;
env->GetJavaVM(&g_vm);//をクリックしてJavaVMポインタを取得します.このポインタを取得し、JavaVMを保存します.(転写)
②(公共性)先了解
TLS(thread-local storage)
スレッドは実行されるユニットであり、同じプロセス内の複数のスレッドがプロセスのアドレス空間を共有し、スレッドには通常独自のスタックがあるが、グローバル変数を実現するには異なるスレッド間で異なる値を取り、影響を受けない.1つの方法は、この変数の読み書きに臨界領域や反発量を加えるなどのスレッドの同期メカニズムを採用することであるが、これは効率を犠牲にする代わりに、ロックをかけないことができるだろうか.スレッドローカルストレージはこれを実行します.
次の2つの問題を解決します.
1まずグローバル変数を定義
2次に、あるスレッドを呼び出す関数で定義します.(スレッドのプロセスにおけるリソースの共通性を保証します.この2つの文は、パラメータを開いたスレッドに渡すことです)
3スレッド関数で参照:(スレッド関数がenvとclassに対応することを保証)
三、私たちはネット上でjniに関するプログラム資料をいくつか見ました(*env)->;
linuxではcファイルで「env->」でコンパイルするとこの構造が見つからず、「(*env)->」を使用するか、変更する必要があります.cppファイルは、c++でコンパイルされます.
------------------------------------------------------------------------
一、ハングアップの原因:
jniレイヤのスレッド関数にenvを呼び出す関数を追加するとフリーズします
二、解決方法
第一に、私たちは理解しなければならない.
①(独立性)JNIEnvはスレッドに関する変数である.すなわちスレッドAにはJNIEnv変数があり、スレッドBにもJNIEnv変数があり、スレッドに関するため、AスレッドはBスレッドのJNIEnv構造体変数を使用できない.
では、どのようにして各スレッドJNIEnvの独立性を保証しますか?
1つのjavaオブジェクトは、JNIによってDLLのsend()関数を呼び出してサーバにメッセージを送信し、サーバメッセージが来るのを待たずにすぐに戻ると同時に、JNIインタフェースのポインタJNIEnv*env(仮想マシン環境ポインタ)、jobject objをDLLの変数に保存する.しばらくすると、DLLのメッセージ受信スレッドは、サーバからのメッセージを受信し、保存するenvとobjによって以前のjavaオブジェクトを呼び出す方法(JAVAコールバック方法に相当)を試みる、このメッセージを処理する際にプログラムが突然終了する(クラッシュ).
すなわち、フロントJAVAスレッドはメッセージを送信し、バックグラウンドスレッドはメッセージを処理し、2つの異なるスレッドに帰属し、同じJNIEnv変数を使用することはできない.ここでは、グローバルなJavaVM*ポインタを利用して現在のスレッドのJNIEnv*ポインタを得るメカニズムを利用することができ、C++の2つのスレッドでTLSを使用してローカルストレージを行うのと同様の原理を利用することができる.
具体的な方法:
グローバルなJavaVM変数を取得するには、次の手順に従います.
/* Our VM */JavaVM *g_vm;
env->GetJavaVM(&g_vm);//をクリックしてJavaVMポインタを取得します.このポインタを取得し、JavaVMを保存します.(転写)
②(公共性)先了解
TLS(thread-local storage)
スレッドは実行されるユニットであり、同じプロセス内の複数のスレッドがプロセスのアドレス空間を共有し、スレッドには通常独自のスタックがあるが、グローバル変数を実現するには異なるスレッド間で異なる値を取り、影響を受けない.1つの方法は、この変数の読み書きに臨界領域や反発量を加えるなどのスレッドの同期メカニズムを採用することであるが、これは効率を犠牲にする代わりに、ロックをかけないことができるだろうか.スレッドローカルストレージはこれを実行します.
次の2つの問題を解決します.
1まずグローバル変数を定義
namespace android
{
static JavaVM* gJavaVM = NULL; // Java VM
static jobject gJavaObj = NULL; // Java object , java
......
2次に、あるスレッドを呼び出す関数で定義します.(スレッドのプロセスにおけるリソースの共通性を保証します.この2つの文は、パラメータを開いたスレッドに渡すことです)
JNIEXPORT void JNICALL Java_Test_setEnev(JNIEnv *env, jobject obj)
{
env->GetJavaVM(&gs_jvm); // JVM
// obj DLL , :
gs_object=env->NewGlobalRef(obj);
HANDLE ht=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFun,0,NULL,NULL);
}
3スレッド関数で参照:(スレッド関数がenvとclassに対応することを保証)
void WINAPI ThreadFun(PVOID argv)//JNI
{
JNIEnv *env;
gs_jvm->AttachCurrentThread((void **)&env, NULL); //
jclass cls = env->GetObjectClass(gs_object); // JAVA
jfieldID fieldPtr = env->GetFieldID(cls,"value","I"); // JAVA
while(1)
{
Sleep(100);
// JAVA ( JAVA)
env->SetIntField(gs_object,fieldPtr,(jint)gs_i++);
}
}
三、私たちはネット上でjniに関するプログラム資料をいくつか見ました(*env)->;
linuxではcファイルで「env->」でコンパイルするとこの構造が見つからず、「(*env)->」を使用するか、変更する必要があります.cppファイルは、c++でコンパイルされます.
------------------------------------------------------------------------