Android NDK pthreads详细使用

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android NDK pthreads详细使用相关的知识,希望对你有一定的参考价值。

这个pthread.h文件可以在NDK环境里创建子线程,并对线程能够做出互斥所、等待、销毁等控制。

写这个博客的原因是我要写如何使用FFmpeg播放视频,因为同时需要播放音频和视频所以需要开启线程,并设置生产者和消费者的关系。

好了直接上整体

1.开启和销毁线程

pthread_create函数能够创建线程,第一个参数是线程的引用,第二个是线程的属性,一般为NULL,第三个为线程运行的函数,第四个是给线程运行函数的参数

pthread_create又是开启线程,只要运行了这个函数线程就会运行起来,也就是运行第三个参数所代表的函数

pthread_t pthreads;
pthread_create(&pthreads, NULL, threadFunc, (void *) "zzw");

等待线程完成和返回参数,这个如果开启线程只有一个可以不写,但是如果有多个线程这个就必须要写,不写的话只会运行第一个线程

int retvalue;
pthread_join(pthreads,(void**)&retvalue);
if(retvalue!=0){
    __android_log_print(ANDROID_LOG_ERROR,"hello","thread error occurred");
}

我们再来看看线程运行函数,这个他可以获取参数,并且能能够提前结束线程

void threadFunc(void arg){

char* str=(char*)arg;

for(int i=0;i<3;i++){
    __android_log_print(ANDROID_LOG_VERBOSE,"hello","i = %d arg = %s",i,str);
    //线程自杀,需要返回参数
    //pthread_exit((void*)2);
    //线程他杀
    //pthread_cancel()
}
return (void *) 0;

}

完整例子代码

#include <jni.h>
#include <string>
#include <android/log.h>
#define LOGE(FORMAT,...) android_log_print(ANDROID_LOG_ERROR,"LC XXX",FORMAT,##VA_ARGS__);

extern "C"
JNIEXPORT jstring
JNICALL
Java_com_example_zth_ndkthread_MainActivity_stringFromJNI(
JNIEnv env,
jobject /
this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}

void threadFunc(void arg){

char* str=(char*)arg;

for(int i=0;i<3;i++){
    __android_log_print(ANDROID_LOG_VERBOSE,"hello","i = %d arg = %s",i,str);
    //线程自杀,需要返回参数
    //pthread_exit((void*)2);
    //线程他杀
    //pthread_cancel()
}
return (void *) 0;

}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_zth_ndkthread_MainActivity_startNativeThread(JNIEnv* env, jobject thiz,jint count) {

pthread_t pthreads;
pthread_create(&pthreads, NULL, threadFunc, (void *) "zzw");

int retvalue;
pthread_join(pthreads,(void**)&retvalue);
if(retvalue!=0){
    __android_log_print(ANDROID_LOG_ERROR,"hello","thread error occurred");
}

}

2.互斥锁

互斥锁指的是它能够锁住一段代码,使得这段代码在解锁之前不能再被执行一次,

初始化

pthread_mutex_t pthread_mutex;
if(pthread_mutex_init(&pthread_mutex,NULL)!=0)
    return;

开启线程时把互斥锁传给线程运行函数

for(int i=0;i<count;i++){
    pthread_create(&pthreads[i],NULL,threadFunc,&pthread_mutex);
}

我们再来看看线程运行函数
取出互斥锁并上锁

pthread_mutex_t* pthread_mutex=(pthread_mutex_t*)arg;
pthread_mutex_lock(pthread_mutex);

然后一段代码

for(int i=0;i<3;i++){
    __android_log_print(ANDROID_LOG_VERBOSE,"hello","i = %d",i);
}
__android_log_print(ANDROID_LOG_VERBOSE,"hello","————————————");

解锁

pthread_mutex_unlock(pthread_mutex);

最后销毁互斥锁

pthread_mutex_destroy(&pthread_mutex);

运行效果如下

03-02 14:25:58.346 10022-10077/com.example.zth.ndkthread V/hello: i = 0
03-02 14:25:58.346 10022-10077/com.example.zth.ndkthread V/hello: i = 1
03-02 14:25:58.346 10022-10077/com.example.zth.ndkthread V/hello: i = 2
03-02 14:25:58.346 10022-10077/com.example.zth.ndkthread V/hello: ------------------------
03-02 14:25:58.346 10022-10078/com.example.zth.ndkthread V/hello: i = 0
03-02 14:25:58.346 10022-10078/com.example.zth.ndkthread V/hello: i = 1
03-02 14:25:58.346 10022-10078/com.example.zth.ndkthread V/hello: i = 2
03-02 14:25:58.346 10022-10078/com.example.zth.ndkthread V/hello: ------------------------
03-02 14:25:58.347 10022-10079/com.example.zth.ndkthread V/hello: i = 0
03-02 14:25:58.347 10022-10079/com.example.zth.ndkthread V/hello: i = 1
03-02 14:25:58.347 10022-10079/com.example.zth.ndkthread V/hello: i = 2
03-02 14:25:58.347 10022-10079/com.example.zth.ndkthread V/hello: ————————————

如果我们没有加锁呢

pthread_mutex_t* pthread_mutex=(pthread_mutex_t*)arg;

// pthread_mutex_lock(pthread_mutex);
for(int i=0;i<3;i++){
android_log_print(ANDROID_LOG_VERBOSE,"hello","i = %d",i);
}
android_log_print(ANDROID_LOG_VERBOSE,"hello","------------------------");
// pthread_mutex_unlock(pthread_mutex);

结果如下

03-02 14:36:50.035 13815-13993/com.example.zth.ndkthread V/hello: i = 0
03-02 14:36:50.035 13815-13993/com.example.zth.ndkthread V/hello: i = 1
03-02 14:36:50.035 13815-13993/com.example.zth.ndkthread V/hello: i = 2
03-02 14:36:50.035 13815-13993/com.example.zth.ndkthread V/hello: ------------------------
03-02 14:36:50.035 13815-13994/com.example.zth.ndkthread V/hello: i = 0
03-02 14:36:50.035 13815-13994/com.example.zth.ndkthread V/hello: i = 1
03-02 14:36:50.035 13815-13994/com.example.zth.ndkthread V/hello: i = 2
03-02 14:36:50.035 13815-13995/com.example.zth.ndkthread V/hello: i = 0
03-02 14:36:50.035 13815-13994/com.example.zth.ndkthread V/hello: ------------------------
03-02 14:36:50.035 13815-13995/com.example.zth.ndkthread V/hello: i = 1
03-02 14:36:50.035 13815-13995/com.example.zth.ndkthread V/hello: i = 2
03-02 14:36:50.035 13815-13995/com.example.zth.ndkthread V/hello: ------------------------

所以互斥锁是先让一个线程做完,然后另外一个线程做。

例子代码:

#include <jni.h>
#include <string>
#include <android/log.h>
#include "pthread.h"
#define LOGE(FORMAT,...) android_log_print(ANDROID_LOG_ERROR,"LC XXX",FORMAT,##VA_ARGS__);

extern "C"
JNIEXPORT jstring
JNICALL
Java_com_example_zth_ndkthread_MainActivity_stringFromJNI(
JNIEnv env,
jobject /
this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}

void threadFunc(void arg){

pthread_mutex_t* pthread_mutex=(pthread_mutex_t*)arg;
pthread_mutex_lock(pthread_mutex);
for(int i=0;i<3;i++){
    __android_log_print(ANDROID_LOG_VERBOSE,"hello","i = %d",i);
}
__android_log_print(ANDROID_LOG_VERBOSE,"hello","------------------------");
pthread_mutex_unlock(pthread_mutex);
return (void *) 0;

}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_zth_ndkthread_MainActivity_startNativeThread(JNIEnv* env, jobject thiz,jint count) {

pthread_mutex_t pthread_mutex;
if(pthread_mutex_init(&pthread_mutex,NULL)!=0)
    return;

pthread_t pthreads[count];
for(int i=0;i<count;i++){
    pthread_create(&pthreads[i],NULL,threadFunc,&pthread_mutex);
}

for(int i=0;i<count;i++){
    int retvalue=0;
    pthread_join(pthreads[i],(void**)&retvalue);
    if(retvalue!=0){
        __android_log_print(ANDROID_LOG_ERROR,"hello","thread error occurred");
    }
}

pthread_mutex_destroy(&pthread_mutex);

}

3.条件变量

视频解码的绘制使用的就是生产者—消费者的模式。比如说我们生产者生成的产品,放到一个队列里面,当生产者生产出产品的时候就会发送信号通知消费者去消费

这个条件变量能够唤醒线程运行

初始化

pthread_cond_init(&c,NULL);

开启生成者线程和消费者线程

pthread_create(&thread_producer, NULL, produce, (void *) "producer");
pthread_create(&thread_comsumer, NULL, comsume, (void *) "comsumer");

循环生产产品,然后提醒消费者

for(;;){
    pthread_mutex_lock(&m);
    productNum++;
    __android_log_print(ANDROID_LOG_VERBOSE,"hello","i = %d",productNum);
    pthread_cond_signal(&c);
    pthread_mutex_unlock(&m);

}

消费者线程如果发现没有产品就等待条件变量提醒,,如果有产品就消费掉

    pthread_mutex_lock(&m);
    while(productNum == 0){
        pthread_cond_wait(&c,&m);

    }
    productNum--;
    __android_log_print(ANDROID_LOG_VERBOSE,"hello","i = %d",productNum);
    pthread_mutex_unlock(&m);

注意生成者与消费者线程运行的全过程都在互斥锁下,都是按顺序一一执行的,这样对于全局变量productNum的计算就不会错误,并且通过一个线程执行pthread_cond_signal来触发另一个线程执行

例子代码

#include <jni.h>
#include <string>
#include <android/log.h>
#include "pthread.h"
#define LOGE(FORMAT,...) android_log_print(ANDROID_LOG_ERROR,"LC XXX",FORMAT,##VA_ARGS__);

extern "C"
JNIEXPORT jstring
JNICALL
Java_com_example_zth_ndkthread_MainActivity_stringFromJNI(
JNIEnv env,
jobject /
this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}

int productNum = 0;
pthread_mutex_t m;
pthread_cond_t c;

void produce(void arg){
char no = (char)arg;
for(;;){
pthread_mutex_lock(&m);
productNum++;
__android_log_print(ANDROID_LOG_VERBOSE,"hello","i = %d",productNum);
pthread_cond_signal(&c);
pthread_mutex_unlock(&m);

}

}

void comsume(void arg){
char no = (char)arg;
for(;;){
pthread_mutex_lock(&m);
while(productNum == 0){
pthread_cond_wait(&c,&m);

    }
    productNum--;
    __android_log_print(ANDROID_LOG_VERBOSE,"hello","i = %d",productNum);
    pthread_mutex_unlock(&m);

}

}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_zth_ndkthread_MainActivity_startNativeThread(JNIEnv* env, jobject thiz,jint count) {

pthread_mutex_init(&m,NULL);
pthread_cond_init(&c,NULL);

pthread_t thread_producer;
pthread_t thread_comsumer;

pthread_create(&thread_producer, NULL, produce, (void *) "producer");
pthread_create(&thread_comsumer, NULL, comsume, (void *) "comsumer");

pthread_join(thread_producer,NULL);
pthread_join(thread_comsumer,NULL);

pthread_mutex_destroy(&m);
pthread_cond_destroy(&c);

}

参考文章

https://www.jianshu.com/p/453d12c16885

http://blog.csdn.net/lxmhuendan/article/details/11967593

以上是关于Android NDK pthreads详细使用的主要内容,如果未能解决你的问题,请参考以下文章

错误记录Android Studio 编译报错 ( VirtualApp 编译 NDK 报错 | Error:A problem occurred configuring project ‘: )(代

错误记录Android Studio 编译报错 ( VirtualApp 编译 NDK 报错 | Error:A problem occurred configuring project ‘: )(代

Android 安装包优化动态库打包配置 ( “armeabi-v7a“, “arm64-v8a“, “x86“, “x86_64“ APK 打包 CPU 指令集配置 | NDK 完整配置参考 )(代

Android NDK - 多线程正在减慢渲染速度

对于某些构建目标,NDK构建器r15既未找到HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC,也未找到pthread_condattr_setclock;构建失败

Android 中的 NDK 到底是什么?(详细解析+案例实战)