Android NDK pthread 生产者消费者线程模型

本文介绍学习和开发移动端音频视频开发必备技能的NDK开发以及基于Linux Pthread 实现生产者和消费者模式的笔记。从Android Stuido下NDK开发环境和基础设置开始。

1.项目搭建

1.1 环境和工具

NDK版本:r23

Android Stuido版本 4.4

开发语言: c++ Kotlin

1.2 项目建立

新建Android 工程,模版选择Native c++

填写包名等基本信息

选择基本C++ standard为工具链默认,这里也用不到c++新标准

完成后就会新建项目,等待Gradle完成sync就可以继续了。

2 NDK项目基本结构

不同于纯SDK项目,NDK项目多了cpp的Soure目录,其中有cpp文件和CMakeLists.txt,可以看出android studio是通过CMake来实现编译C++代码的。这个CMake文件可以用来加入新的cpp source文件,引用三方头文件,链接各种三方c库

    cmake_minimum_required(VERSION 3.10.2)
    # Declares and names the project.
    project("pthread")
    # Creates and names a library, sets it as either STATIC
    # or SHARED, and provides the relative paths to its source code.
    # You can define multiple libraries, and CMake builds them for you.
    # Gradle automatically packages shared libraries with your APK.
    add_library( # Sets the name of the library.
                 native-lib
                 # Sets the library as a shared library.
                 SHARED
                 # Provides a relative path to your source file(s).
                 native-lib.cpp )
    # Searches for a specified prebuilt library and stores the path as a
    # variable. Because CMake includes system libraries in the search path by
    # default, you only need to specify the name of the public NDK library
    # you want to add. CMake verifies that the library exists before
    # completing its build.
    find_library( # Sets the name of the path variable.
                  log-lib

                  # Specifies the name of the NDK library that
                  # you want CMake to locate.
                  log )

    # Specifies libraries CMake should link to your target library. You
    # can link multiple libraries, such as libraries you define in this
    # build script, prebuilt third-party libraries, or system libraries.

    target_link_libraries( # Specifies the target library.
                           native-lib

                           # Links the target library to the log library
                           # included in the NDK.
                           ${log-lib} )

之所以能编译,可以看下build.gradle中多了如下代码

# defaultconfig中:
    externalNativeBuild {
        cmake {
            cppFlags ''
        }
    }
    
    ## android {}
    externalNativeBuild {
        cmake {
            path file('src/main/cpp/CMakeLists.txt')
            version '3.10.2'
        }
    }

3 Kotlin 调用c++

Kotlin作为一个JVM下运行的语言,也具备有Java一样和C++互操作的能力,也是遵循JNI规范的,具体为另外说明Kotlin和c++的交互,这里简单的看下

// 类似Java 的natvie 关键字,kotilin是用external表示外部的原生方法,就是c++方法

    external fun nativeThread()
    // companion object 修饰为伴生对象,伴生对象在类中只能存在一个,类似于java中的静态方法 Java 中使用类访问静态成员,静态方法。
    companion object {
        // Used to load the 'native-lib' library on application startup.
        init {
            // 加载c库,so文件,系统会根据架构区加载,这的名字就是cmake中的add_library的名字,如果是引用一句存在的c库,就是libxxx.so
            System.loadLibrary("native-lib")
        }
    }
    

看下c++文件,按照JNI规范来实现

// 必须extern “C” 否则c++编译产生的符号会不一样造成不能调用或者被其他c库链接
// 可以看出jni规范,这里函数的命名就是包名+类名+方法名,不过就是点换成下划下线,JNIEnv jobct thiz作用另问说明和jvm相关
extern "C"
JNIEXPORT void JNICALL
Java_com_example_pthread_MainActivity_nativeThread(JNIEnv *env, jobject thiz) {

}

4.生产者消费者实现

4.1 pthread

pthread是符合可移植操作系统接口的线程规范,Linux自然是实现了,android上c++的多线程方案本质就是基于pthread实现。

4.2 pthead几个重要的对象和函数

4.2.1 pthread_t

表示一个线程,可以通过pthread_create创建

    int pthread_create(pthread_t* __pthread_ptr, pthread_attr_t const* __attr, void* (*__start_routine)(void*), void*);

参数2 pthread_attr_t是设置线程的一些属性的,这里不详细说明,可以查询相关文档,没有可以NULL

参数3 是线程真正的实现方法,其实类似Java Runable接口,是一个
void fun(vod test)的函数

参数4 用来传递参数,void* 多个参数可以定义结构体,使用指针的方式传参

pthread_exit(): 用来退出线程,同时每个线程退出必须return,否则会出错

4.2.2 pthread_mutex_t

互斥锁,当线程需要竞争访问一个资源时候,需要锁定,可以通过pthread_mutex_init 创建,通过pthread_mutex_lock锁,pthread_mutex_unlock解锁,两个线程访问同一个对象是先获得pthread_mutex_t的继续执行,其他等待释放

4.2.3 pthread_cond_t

条件量,可以通过进行等待,当满足某个条件或者收到某个条件时候放开。pthread_cond_init 创建,pthread_cond_signal发信号,pthread_cond_wait等待并放开互斥锁

4.3 生产者消费者实现

思路:两条线程,一个生产者,一个消费者,队列采用stl中的queue对象,pthread_mutex_t pthread_cond_t 实现等待和放开

#include <jni.h>
#include <string>
#include <android/log.h>
#include "pthread.h"
#include "queue"
#include "unistd.h"
// 定义Log
#define LOG_TAG "FromNDK"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

// 生产者进程
pthread_t produceThread;
// 消费者进程
pthread_t consumeThread;
// 条件信号
pthread_cond_t cond;
// 队列
std::queue<int> queue;
// 互斥
pthread_mutex_t mutex;

// 生产者实现
void* producer(void* args) {

    LOGI("start producer thread");

    while (1) {
        // 加锁。因为queue公用
        pthread_mutex_lock(&mutex);
        for(int i=0;i<10;i++) {
            LOGI("start producer produce %d",i);
            //推入
            queue.push(i);
        }
        // 放开
        pthread_mutex_unlock(&mutex);
        // 模拟休眠和慢
        usleep(1000* 10 * 1000);
        // 开始又开始生产了,通知消费者
        pthread_cond_signal(&cond);
    }
    return 0;
}

// 消费者实现
void *consume(void * args) {
    while (1) {
        // 加锁。因为queue公用
        pthread_mutex_lock(&mutex);
        // 队列里又消息,则消费
        if(queue.size() > 0) {
            LOGI("now consume %d",queue.front());
            queue.pop();
        } else {
            // 队列空了,等条件并放开互斥
            pthread_cond_wait(&cond,&mutex);
        }
        // 放开互斥锁
        pthread_mutex_unlock(&mutex);
    }
    return 0;
}
// JNI给到Kotlin调用
extern "C"
JNIEXPORT void JNICALL
Java_com_example_pthread_MainActivity_nativeThread(JNIEnv *env, jobject thiz) {
    // 初始化
    pthread_mutex_init(&mutex,NULL);
    pthread_cond_init(&cond,NULL);
    for(int i=0;i<10;i++) {
        queue.push(i);
    }

    pthread_create(&produceThread,NULL,producer,NULL);

    pthread_create(&consumeThread,NULL,consume,NULL);
}

Lokie博客
请先登录后发表评论
  • 最新评论
  • 总共0条评论
  • 本博客使用免费开源的 laravel-bjyblog v5.5.1.1 搭建 © 2014-2018 lokie.wang 版权所有 ICP证:沪ICP备18016993号
  • 联系邮箱:kitche1985@hotmail.com