这个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