NDK的多线程编程<pthread.h>库的使用问题

问题

在项目中使用一个开源库,包含原生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版本的执行结果是:

Android版本 logcat 执行结果
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 called on a destroyed mutex (0xb511d6f8) 应用崩溃
11 A/libc: FORTIFY: pthread_mutex_trylock called on a destroyed mutex (0xb511d6f8) 应用崩溃

虽然只选了四个版本,没有尝试更多版本,也没用比对不同品牌,只测试模拟器和两个品牌的手机,但是可以看出,随着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版本 logcat 执行结果
centos 7.9 Linux version 3.10.0 pthread_mutex_trylock 0xf31010 22 加锁失败
Ubuntu 20.04.3 Linux version 5.11.0 pthread_mutex_trylock 0x556bcaa062a0 22 加锁失败

可以看到虽然Linux版本版本不相同,结果都是加锁失败,但是不会出现运行时错误。

总结

通过对比互斥锁异常加锁的测试结果可以看出,Android底层的<pthread.h>库有自己的独特实现,和其他Linux发行版本并不一致,并且Android的版本越高,<pthread.h>库的执行越严格,可能会导致应用直接崩溃。

所以为了保证NDK程序的最大兼容性和健壮性,使用<pthread.h>库来实现线程同步的过程中,一定要保证互斥锁的应用遵守正常的使用流程,防止出现类似于互斥锁被销毁之后又重新加锁的情况。

猜你喜欢

转载自blog.csdn.net/hanshiying007/article/details/121810054