Android JNI开发系列:第七章 POSIX线程

    xiaoxiao2025-08-26  9

    项目代码:https://github.com/VincentWei95/ndk

    Android JNI开发系列:第一章 JNIEnv接口指针

    Android JNI开发系列:第二章 数据类型

    Android JNI开发系列:第三章 对引用数据类型的操作

    Android JNI开发系列:第四章 异常处理

    Android JNI开发系列:第五章 局部和全局引用

    Android JNI开发系列:第六章 线程

    Android JNI开发系列:第七章 POSIX线程

    Android JNI开发系列:第八章 POSIX Socket API 面向连接的通信

    Android JNI开发系列:第九章 POSIX Socket API 无连接的通信

    Android JNI开发系列:第十章 POSIX Socket API 本地通信

    POSIX线程也被简称为pthreads,是一个线程的POSIX标准。许多主流操作系统,包括windows、mac、linux提供满足POSIX线程标准的多线程支持。

    1 在原生代码中使用POSIX线程

    1.1 头文件

    通过 pthread.h 头文件声明POSIX Thread APIs。

    #include <pthread.h>

    1.2 使用pthread_create创建线程

    // 创建成功返回0,否则返回一个错误代码 int pthread_create(pthread_t* thread, pthread_attr_t const* attr, void* (*start_routine)(void*), void* arg);

    参数说明:

    pthread_t* thread:指向thread_t类型变量的指针,函数用该指针返回新线程句柄

    pthread_attr_t const* attr:指向pthread_attr_t结构的指针形式存在的新线程属性,可以通过该属性指定新线程栈基址、栈大小、守护大小、调度策略和调度优先级等

    void* (start_routine)(void ):指向线程启动程序的函数指针,启动程序将线程参数看成是void指针,返回void指针类型结果;当线程以空指针的形式存在时,参数都需要被传递给启动程序,如果不需要传递参数,它可以为NULL【简单理解,void * (start_routine)(void)参数传递的就是一个线程执行方法,比如void produce(void data)】

    void* produce(void* data) { ... } pthread_t pthread; pthread_create(&pthread, NULL, produce, NULL)

    1.3 从POSIX线程返回结果

    // 它将挂起调用线程的执行,直到目标线程终止 // 如果ret_val不是NULL,该函数将ret_val指针的值设置为启动程序的返回结果 // 如果成功,pthread_join函数返回0,否则返回错误代码 int pthread_join(pthread_t thread, void** ret_val);

    参数说明:

    pthread_t thread:线程句柄,它是pthread_create函数返回的目标线程

    void** ret_val:指向空指针的指针,该指针是为了从启动程序中获得返回值

    2 POSIX线程同步

    和java线程相似,POSIX线程API也提供同步功能。POSIX提供两个最常用的同步机制:

    互斥锁(Mutexes)确保代码的互斥执行,即代码的特定部分不同时执行

    信号量(Semaphores)控制对特定数目可用资源的访问,如果没有可用资源,调用线程只是在信号量所涉及的资源上等待,直到资源可用

    2.1 用互斥锁同步POSIX线程

    POSIX线程API通过 pthread_mutex_t 数据类型展示互斥锁到原生代码。POSIX线程API从原生代码提供一组交互功能的互斥。使用前,互斥变量应该先被初始化。

    初始化互斥锁

    POSIX线程API提供两种初始化互斥锁的方法:pthread_mutex_init 函数和 PTHREAD_MUTEX_INITIALIZED 宏。

    // 初始化成功,互斥锁初始化并处于锁打开状态,函数返回0,否则返回错误代码 int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr);

    参数说明:

    1、pthread_mutex_t* mutex:指向要初始化的互斥变量的指针

    2、pthread_mutexattr_t* attr:一个指向为互斥锁定义属性的pthread_mutexattr_t结构体的指针;如果设置为NULL,将使用默认属性。如果第二个参数 pthread_mutexattr_t* attr 设置为NULL使用默认属性且默认属性够用,PTHREAD_MUTEX_INITIALIZED 宏比pthread_mutex_init 函数更合适:

    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 锁定互斥锁

    pthread_mutex_lock 函数可以通过对一个已经初始化的互斥锁进行封锁操作达到互斥操作的目的。

    // 调用线程被挂起直到互斥锁被打开,如果成功函数返回0,否则返回错误代码 int pthread_mutex_lock(pthread_mutex_t* mutex); 解锁互斥锁 // 调用策略决定解锁后执行哪个等待互斥锁的线程(即解锁后由调度策略决定往后的哪个线程会持有锁,与java的多线程差不多) // 如果成功函数返回0,否则返回错误代码 int pthread_mutex_unlock(pthread_mutex_t* mutex); 销毁互斥锁 // 注意:试图销毁一个锁着的变量将返回不确定结果,所以在执行销毁互斥锁时,要确保已经解锁 int pthread_mutex_destroy(pthread_mutex_t* mutex);

    2.2 使用信号量同步POSIX线程

    和其他POSIX函数不同,POSIX信号量在不同的头文件 semaphore.h 中声明。

    #include <semaphor.h>

    POSIX信号量是 sem_t 类型,POSIX Semaphore API提供了一组与原生代码中的信号量交互的函数,在使用之前,信号量变量应该先被初始化。

    初始化信号量 // 如果成功函数返回0,否则返回-1 // sem_t* sem:将被初始化的信号量变量指针 // int pshared:共享标志 // unsigned int value:初始值 extern int sem_init(sem_t* sem, int pshared, unsigned int value); 锁定信号量

    一旦信号量被正确地初始化了,线程可以使用 sem_wait 函数减少信号量的数量。

    // 如果成功函数返回0,否则返回-1 // 如果sem_t* sem信号量变量指针的信号量值大于0,上锁成功,并且信号量也会相应递减;如果信号量的值是0,调用线程被挂起,直到另一个线程通过解锁它增加信号量的值 extern int sem_wait(sem_t* sem); 解锁信号量 // 如果成功函数返回0,否则返回-1 // sem_post函数解锁信号量后,信号量的值会增加1 // 调用策略决定信号量解锁后执行哪个等待线程 extern int sem_post(sem_t* sem); 销毁信号量 // 如果成功函数返回0,否则返回-1 // 销毁一个正在阻塞的信号量有可能导致未知行为,所以销毁信号量前,一定要先解锁信号量 extern int sem_destroy(sem_t* sem);

    3 POSIX线程的优先级和调度策略

    3.1 POSIX线程调度策略

    最经常使用的调度策略如下:

    SCHED_FIFO:先进先出调度策略基于线程进入列表的时间进行排序,也可以基于优先级在列表中移动线程

    SCHED_RR:循环轮转调度策略是线程执行时间加以限制的SCHED_FIFO,其目的是避免线程独占可用的CPU时间

    这些调用策略常量在 sched.h 头文件中定义。

    调用策略的使用地点:

    用 pthread_create 函数创建一个新线程时,用线程属性结构 pthread_attr_t 的 sched_policy 域来定义调度策略: int pthread_create(pthread_t* thread, pthread_attr_t const* attr, void* (*start_routine)(void*), void* arg); 用 pthread_setschedparam 函数定义调度策略: // pthread_t thid:指向目标线程句柄的指针 // int policy:调度策略 // struct sched_param const* param:调度策略所需参数 int pthread_setschedparam(pthread_t thid, int policy, struct sched_param const* param);

    3.2 POSIX Thread优先级

    线程优先级的使用地点:

    用 pthread_create 函数创建一个新线程时,用线程属性结构 pthread_attr_t 的 sched_priority 域来定义调度优先级 int pthread_create(pthread_t* thread, pthread_attr_t const* attr, void* (*start_routine)(void*), void* arg); 用 pthread_setschedparam 函数在 sched_param 结构体中提供优先级

    优先级的最大值和最小值的取值,取决于所使用的调度策略,应用程序可以使用 sched_get_priority_max 函数和 sched_get_priority_min 函数查询这些数。

    3.3 POSIX线程同步实现生产者消费者demo

    com_example_ndk_PosixTest.h /* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> #include "com_example_ndk_JNITest.h" #include "../../../../../../../sdk/ndk-bundle/toolchains/llvm/prebuilt/windows-x86_64/sysroot/usr/include/jni.h" /* Header for class com_example_ndk_PosixTest */ #ifndef _Included_com_example_ndk_PosixTest #define _Included_com_example_ndk_PosixTest #ifdef __cplusplus extern "C" { #endif void * produce(void *data); void * consume(void *data); /* * Class: com_example_ndk_PosixTest * Method: nativeInit * Signature: ()V */ JNIEXPORT void JNICALL Java_com_example_ndk_PosixTest_nativeInit (JNIEnv *, jobject); /* * Class: com_example_ndk_PosixTest * Method: nativeFree * Signature: ()V */ JNIEXPORT void JNICALL Java_com_example_ndk_PosixTest_nativeFree (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif com_example_ndk_PosixTest.c #include "/include/com_example_ndk_JNITest.h" #include <jni.h> #include <stdio.h> #include <syslog.h> #include <pthread.h> #include <unistd.h> #define PRODUCE "1" #define CONSUME "0" #define SLEEP_TIMEOUT 10000 pthread_t producer_thread; pthread_t consumer_thread; pthread_mutex_t mutex; pthread_cond_t cond; jstring value = PRODUCE; void * produce(void *data) { while (1) { pthread_mutex_lock(&mutex); // 当前value已生产,等待 if (value == PRODUCE) { pthread_cond_wait(&cond, &mutex); } else { syslog(LOG_INFO, "posix produce product"); value = PRODUCE; pthread_cond_signal(&cond); } pthread_mutex_unlock(&mutex); usleep(SLEEP_TIMEOUT); } } void* consume(void *data) { while (1) { pthread_mutex_lock(&mutex); // 当前value已消费,等待 if (value == CONSUME) { pthread_cond_wait(&cond, &mutex); } else { syslog(LOG_INFO, "posix consume product"); value = CONSUME; pthread_cond_signal(&cond); } pthread_mutex_unlock(&mutex); usleep(SLEEP_TIMEOUT); } } /* * Class: com_example_ndk_PosixTest * Method: nativeInit * Signature: ()V */ JNIEXPORT void JNICALL Java_com_example_ndk_PosixTest_nativeInit (JNIEnv *env, jobject object) { pthread_mutex_init(&mutex, NULL); pthread_cond_init(&cond, NULL); pthread_create(&producer_thread, NULL, produce, NULL); pthread_create(&consumer_thread, NULL, consume, NULL); } /* * Class: com_example_ndk_PosixTest * Method: nativeFree * Signature: ()V */ JNIEXPORT void JNICALL Java_com_example_ndk_PosixTest_nativeFree (JNIEnv *env, jobject object) { pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond); } PosixTest.java package com.example.ndk; public class PosixTest { public native void nativeInit(); public native void nativeFree(); } PosixActivity.java package com.example.ndk; import android.os.Bundle; import android.os.Looper; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.view.View; /** * 使用POSIX线程实现生产者消费者 */ public class PosixActivity extends AppCompatActivity { private PosixTest mPosixTest; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_posix); mPosixTest = new PosixTest(); findViewById(R.id.btn_start).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mPosixTest.nativeInit(); } }); } @Override protected void onStop() { super.onStop(); if (isFinishing()) { mPosixTest.nativeFree(); } } }
    最新回复(0)