• 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

  • 相关阅读:
    闭包 与 装饰器
    Linux常用命令 (二)
    day1 linux常用命令(一)
    📎 .xib
    📎 Emoji 前端转换
    📎 钉钉微应用( 新启项目Weex H5 )
    📎 ROR:常用GEM
    📎 AndroidNative【ING...】
    🆕 ror方法
    安装centos7
  • 原文地址:https://www.cnblogs.com/jianpanwuzhe/p/8493464.html
Copyright © 2020-2023  润新知