質問
プロジェクトでは、同時操作が多いネイティブ C++ コードを含むオープン ソース ライブラリが使用され、<pthread.h> ライブラリを使用してスレッド セーフが確保されます。
その中で、スレッド同期は相互排他ロックを使用して実現されます。通常のプロセス状況は次のとおりです。
- ロックを初期化します – pthread_mutex_init
- ロック – pthread_mutex_lock または pthread_mutex_trylock
- ロック解除 – pthread_mutex_unlock
- 破壊ロック – pthread_mutex_destroy
ただし、ロジックが複雑なため、実行例外が発生する場合があり、ロックが破棄された後にロックが追加されます。
次に、さまざまなテスト マシンでさまざまなパフォーマンスが得られます。一部は正常に実行でき、一部はスタックし、一部は直接クラッシュすることさえあります。
A/libc: FORTIFY: pthread_mutex_trylock called on a destroyed mutex (0xb511d6f8)
A/libc: Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 2775 (han.mynativeapp), pid 2775 (han.mynativeapp)
理由を分析する
スレッド同期例外の原因を分析するには、次のコードを使用して異常な状況をシミュレートします。
#include <jni.h>
#include <string>
#include <malloc.h>
#include <pthread.h>
#include <android/log.h>
extern "C" JNIEXPORT jstring JNICALL
Java_com_shiyinghan_mynativeapp_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
pthread_mutex_t *mutex = static_cast<pthread_mutex_t *>(calloc(1, sizeof(*mutex)));
pthread_mutex_init(mutex, NULL);
pthread_mutex_lock(mutex);
pthread_mutex_unlock(mutex);
pthread_mutex_destroy(mutex);
int r = pthread_mutex_trylock(mutex);
__android_log_print(ANDROID_LOG_INFO,"stringFromJNI","pthread_mutex_trylock %p %d", mutex, r);
return env->NewStringUTF(hello.c_str());
}
次に、さまざまな Android バージョンを使用した実行結果は次のとおりです。
アンドロイド版 | ログキャット | の結果 |
---|---|---|
6.0 | I/stringFromJNI: pthread_mutex_trylock 0x557ef45d20 0 | ロック成功 |
8.1 | I/stringFromJNI: pthread_mutex_trylock 0x557f13cc30 16 | ロックに失敗しました |
10 | A/libc: FORTIFY: 破棄されたミューテックスで呼び出された pthread_mutex_trylock (0xb511d6f8) | アプリがクラッシュする |
11 | A/libc: FORTIFY: 破棄されたミューテックスで呼び出された pthread_mutex_trylock (0xb511d6f8) | アプリがクラッシュする |
4 つのバージョンのみが選択されましたが、それ以上のバージョンは試しませんでしたし、さまざまなブランドを比較するためにも使用しませんでした.エミュレーターと 2 つのブランドの携帯電話のみをテストしましたが、Android のバージョンが増えるにつれて、 <pthread.h> ライブラリ、pthread ミューテックスの強制がより厳しくなっています。pthread_mutex_destroy がロックを破棄した後、下位バージョンは引き続き正常にロックできますが、わずかに上位バージョンはロックに失敗し、最新バージョンは直接アプリケーションをクラッシュさせます。
Linux でのテスト
Android の最下層は Linux カーネルです.Linux で直接実行するとどうなりますか?コードは次のとおりです:
#include <stdio.h>
#include <malloc.h>
#include <pthread.h>
int main() {
pthread_mutex_t *mutex = calloc(1, sizeof(*mutex));
pthread_mutex_init(mutex, NULL);
pthread_mutex_lock(mutex);
pthread_mutex_unlock(mutex);
pthread_mutex_destroy(mutex);
int r = pthread_mutex_trylock(mutex);
printf("pthread_mutex_trylock %p %d\n", mutex, r);
return 0;
}
実行結果は次のとおりです。
Linux版 | ログキャット | の結果 |
---|---|---|
centos 7.9 Linux バージョン 3.10.0 | pthread_mutex_trylock 0xf31010 22 | ロックに失敗しました |
Ubuntu 20.04.3 Linux バージョン 5.11.0 | pthread_mutex_trylock 0x556bcaa062a0 22 | ロックに失敗しました |
Linux のバージョンは異なりますが、結果的にロックに失敗していることがわかりますが、ランタイム エラーは発生していません。
要約する
ミューテックスの異常ロックのテスト結果を比較すると、Android の最下部にある <pthread.h> ライブラリには独自の実装があり、他の Linux ディストリビューションと矛盾しており、Android のバージョンが高いほど < pthread.h> より厳密な適用を伴うライブラリでは、アプリが完全にクラッシュする可能性があります。
したがって、NDK プログラムの最大限の互換性と堅牢性を確保するために、<pthread.h> ライブラリを使用してスレッド同期を実現するプロセスでは、ミューテックスの適用が通常の使用プロセスに従っていることを確認する必要があります。ミューテックスがブロックされているのと同様の発生を防ぎます. 破壊後に再ロックする場合.