這個pthread.h文件可以在NDK環境里創建子線程,並對線程能夠做出互斥所、等待、銷毀等控制。寫這個博客的原因是我要寫如何使用FFmpeg播放視頻,因為同時需要播放音頻和視頻所以需要開啟線程,並設置生產者和消費者的關係。好了直接上整體 1.開啟和銷毀線程 pthread_create函數能夠創建 ...
這個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