• Android Native Thread


    Android native thread

    本文使用android版本为:Android pie_9.0.0_r3

    在Android的Framework层,使用线程时都是Thread类,究其根本还是POSIX标准的pthread类实现,只不过加入了一些android中特有的特性,比如:继承RefBase,将其纳入只能指针体系。不过因为对pthread进行了二次包装,其具体的线程相关API已和pthread大相径庭。在深入Framework代码时,难免会遇到Thread对象,了解它,对相关代码的理解将会更加顺利。

    Thread定义

    但只推荐理解,不推荐使用。在开发自己程序时,依然只建议使用pthread,因为在Thread类定义的地方,开发者已经做了相关不推荐使用的注释(如下一段代码所示)。至于深层次的原因,暂不得而知。

    // /system/core/include/utils/Thread.h
    namespace android {
    // DO NOT USE: please use std::thread
    class Thread : virtual public RefBase {
    public:
        // 创建线程对象,但并不创建或启动关联线程。
        explicit            Thread(bool canCallJava = true); 
        virtual             ~Thread();
        // 在threadLoop函数中启动线程,threadLoop函数必须被实现
        virtual status_t    run(const char* name, int32_t priority = PRIORITY_DEFAULT, size_t stack = 0);
        // 请求对象的线程退出。 这个函数是异步的,当该函数结束后,线程可能还在运行。
        virtual void        requestExit();
        // 线程运行前执行,可以用于执行一些初始化函数
        virtual status_t    readyToRun();
        // 调用requestExit函数,并且等待对象的线程退出。可能造成死锁
                status_t    requestExitAndWait();
        // 等待直到线程退出。如果线程退出,则该函数立刻return,注意别再对象线程中调用该函数,这样容易导致死锁。
                status_t    join();
        // 用来查看线程是否正在运行
                bool        isRunning() const;
    
    #if defined(__ANDROID__)
        // 和线程调用gettid()函数的调用是一致的,将会返回线程在内核中的ID。如果返回-1则表示线程并未运行。
                pid_t       getTid() const;
    #endif
    
    protected:
        // 如果requestExit()函数被调用,则该函数将返回true
                bool        exitPending() const;
    private:
        // 子类必须实现的函数。是线程生命周期的开始,在Thread对象中,该函数有两种运行模式
        // 1、loop:如果threadLoop() return true, 该函数还将继续被执行,直到requestExit函数被调用
        // 2、once:如果threadLoop() return false, 线程将在该函数return时退出。
        virtual bool        threadLoop() = 0;
        
    private:
        Thread& operator=(const Thread&);
        static  int             _threadLoop(void* user);
        const   bool            mCanCallJava;
        // 读或写时总是持有mLock
                thread_id_t     mThread; // 由创建线程时,pthread_t值的指针类型转换而来
        mutable Mutex           mLock;
                Condition       mThreadExitedCondition;
                status_t        mStatus;
        // 请注意,所有对mExitPending和running的访问都需要持有mLock
        volatile bool           mExitPending;
        volatile bool           mRunning;
                sp      mHoldSelf;
    #if defined(__ANDROID__)
        // legacy for debugging, not used by getTid() as it is set by the child thread
        // and so is not initialized until the child reaches that point
                pid_t           mTid;
    #endif
    };
    }; // namespace android
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    上述是Thread类头文件中对于Thread的定义,我们重点关注构造函数和虚函数实现,即如下函数:

    • `Thread()与~Thread() :构造函数与析构函数
    • onFirstRef():从RefBase处即成的智能指针第一次创建的函数。
    • run:创建线程的函数,一般在threadLoop()函数中被调用
    • readyToRun():线程准备函数
    • _threadLoop():线程循环函数
    • requestExit():请求退出函数

    接下来也会以此为序,对各个重要函数实现作详细解读。

    构造函数与析构函数

    Thread::Thread(bool canCallJava) :   
    		mCanCallJava(canCallJava), mThread(thread_id_t(-1)), mLock("Thread::mLock"),
            mStatus(NO_ERROR), mExitPending(false), mRunning(false)
    #if defined(__ANDROID__)
            , mTid(-1)
    #endif
    {
    }
    
    Thread::~Thread()
    {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    可以看到,构造函数非常简单,都只是初始化一些私有变量,简单了解一下这些变量的作用:

    • mCanCallJava :
    • mStatus :标志着线程运行时的状态
    • mExitPending:标志着线程是否调用过requestExit(),调用过则返回true
    • mRunning:线程是否正在运行

    析构函数更简单,直接啥也没做。相信一些释放资源的工作都分配到其它函数中了。

    onFirstRef()

    当Thread对象创建,并被智能指针(如sp)第一次引用时,就会调用该函数。该函数没有默认实现,但在FrameWork的很多代码中,都会通过重写该函数来初始化一些数据,阅读代码时应该引起足够的重视。

    readyToRun():线程执行准备

    该函数通常是用于使用者在线程运行之前,做一些准备工作的。所以,在默认实现中,直接返回了NO_ERROR,如果子线程有需要可以直接重写,以达到对应目的。

    // /system/core/libutils/Threads.cpp
    status_t Thread::readyToRun()
    {
        return NO_ERROR;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    run():创建线程

    真正创建线程的函数,线程也是从这个函数调用之后开始正式运行的,通常子类会将该函数包装到一个类似start()的函数中,以示线程启动。

    status_t Thread::run(const char* name, int32_t priority, size_t stack) {
        // 当线程发生异常,需要重新尝试时,需要将线程相关状态初始化到最开始的状态。
        mStatus = NO_ERROR;
        mExitPending = false;
        mThread = thread_id_t(-1);
        // 让Thread对象拥有自己的强指针,防止被销毁
        mHoldSelf = this;
        mRunning = true; // 标志线程已经运行,不一定正在运行。自己体会
    
        bool res;
        if (mCanCallJava) {
            res = createThreadEtc(_threadLoop, this, name, priority, stack, &mThread);
        } else { // 将_threadLoop作为线程函数,创建线程
            res = androidCreateRawThreadEtc(_threadLoop, this, name, priority, stack, &mThread);
        }
    
        if (res == false) { // 如果线程创建失败,设置相关的失败状态
            mStatus = UNKNOWN_ERROR;   // something happened!
            mRunning = false;
            mThread = thread_id_t(-1);
            mHoldSelf.clear();  // "this" 引用减一
            return UNKNOWN_ERROR;
        }
        return NO_ERROR;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    可以创建一个新线程的入口有两个:createThreadEtc()androidCreateRawThreadEtc()。决定变量在于mCanCallJava,从变量的字面意思来说,就是如果变量为true,创建的线程可以被java代码调用。如果为false则在本地使用。

    其实createThreadEtc()函数创建线程,最终也会使用 androidCreateRawThreadEtc()函数,只不过createThreadEtc()会在调用 androidCreateRawThreadEtc()函数之前,会去初始化JNI部分,使得java代码可以直接调用该线程。至于如何初始化,这主要是JNI部分的内容,就不在本文中展开了。

    androidCreateRawThreadEtc

    int androidCreateRawThreadEtc(android_thread_func_t entryFunction, // 线程函数
                                   void *userData, // 线程函数对应的参数
                                   const char* threadName __android_unused, // 线程名称
                                   int32_t threadPriority,
                                   size_t threadStackSize, // 线程栈大小
                                   android_thread_id_t *threadId)
    {
        pthread_attr_t attr;
        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // 意味着子线程结束后自行释放资源,不需要主线程等待
    
    #if defined(__ANDROID__)
        if (threadPriority != PRIORITY_DEFAULT || threadName != NULL) {
            thread_data_t* t = new thread_data_t;
            t->priority = threadPriority;
            t->threadName = threadName ? strdup(threadName) : NULL;
            t->entryFunction = entryFunction;
            t->userData = userData;
            entryFunction = (android_thread_func_t)&thread_data_t::trampoline;
            userData = t;
        }
    #endif
    
        if (threadStackSize) { // 如果有指定栈大小,将其设置为线程栈大小
            pthread_attr_setstacksize(&attr, threadStackSize);
        }
    
        errno = 0;
        pthread_t thread; 
        int result = pthread_create(&thread, &attr, (android_pthread_entry)entryFunction, userData);
        pthread_attr_destroy(&attr);
    	// ...
        if (threadId != NULL) {
            *threadId = (android_thread_id_t)thread; // XXX: this is not portable
        }
        return 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    总结一下:

    androidCreateRawThreadEtc()函数的作用是:封装了创建线程的操作。

    run()函数通常调用androidCreateRawThreadEtc(),并为其指定_threadLoop函数指针作为线程函数,最终创建出了一个真正的线程,并通过参数将线程ID存储在mThread变量中。此时,如果一切顺利,mRunning = true

    _threadLoop() : 线程函数

    该函数是真正的线程函数,通过在run()函数中调用androidCreateRawThreadEtc()将该函数设置给pthread线程。

    int Thread::_threadLoop(void* user) { // 线程函数创建时,传递的线程参数就是Thread对象的this指针
        Thread* const self = static_cast(user);
        // ...
        bool first = true;
        do {
            bool result;
            if (first) { // 第一次运行
                first = false; // 只执行一次
                self->mStatus = self->readyToRun(); // 调用Thread对象的readyToRun()函数
                result = (self->mStatus == NO_ERROR);
    			// 如果状态正常,并且没有调用过退出相关函数,则一直循环执行threadLoop()函数。
                if (result && !self->exitPending()) { 
                    result = self->threadLoop();
                }
            } else { // 后续运行,都全走这里
                result = self->threadLoop();
            }
            { // 建立mLock作用域
                Mutex::Autolock _l(self->mLock);
                if (result == false || self->mExitPending) { // 1. 条件满足线程退出
                    self->mExitPending = true;
                    self->mRunning = false;
                    self->mThread = thread_id_t(-1);
                    self->mThreadExitedCondition.broadcast();
                    break;
                }
            }
            // Release our strong reference, to let a chance to the thread
            // to die a peaceful death.
            strong.clear();
            // And immediately, re-acquire a strong reference for the next loop
            strong = weak.promote();
        } while(strong != 0);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    代码中,标记为1处,是线程退出的两种情况:

    • 主动退出:当threadLoop()函数的返回值为false时,线程主动结束。
    • 被动退出:当mExitPending为true时,由外界影响被动退出。mExitPending可以通过requestExit()函数设置为true。

    这其中,threadLoop()函数是一个未被默认实现的函数,必须由子线程实现。通常子线程重写该函数后,会在其中实现主要的线程逻辑,当子线程想要结束线程时,只要该函数返回false即可,如果该函数返回true,并且外界没有调用requteExit()requestExitAndWait()函数,那么threadLoop()函数将会一直循环执行下去。

    requestExit() 线程退出函数

    该函数非常简单,直接看。

    void Thread::requestExit()
    {
        Mutex::Autolock _l(mLock);
        mExitPending = true;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Framework线程示例:StartPropertySetThread

    该类为Thread子类,主要用于修改属性,具体作用就不展开,主要查看一下一个最简单的Thread线程,如何实现。

    头文件:

    // \frameworks\native\services\surfaceflinger\StartPropertySetThread.h
    #include 
    
    #include 
    #include 
    namespace android {
    
    class StartPropertySetThread : public Thread { // 继承自Thread
    public:
        explicit StartPropertySetThread(bool timestampPropertyValue);
        status_t Start();
    private:
        virtual bool threadLoop();
        static constexpr const char* kTimestampProperty = "service.sf.present_timestamp";
        const bool mTimestampPropertyValue;
    };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    具体实现:

    #include 
    #include "StartPropertySetThread.h"
    
    namespace android {
    
    StartPropertySetThread::StartPropertySetThread(bool timestampPropertyValue):
            Thread(false), mTimestampPropertyValue(timestampPropertyValue) {}
    
    status_t StartPropertySetThread::Start() { // 在start函数中,封装了run函数,以创建和运行真正的线程
        return run("SurfaceFlinger::StartPropertySetThread", PRIORITY_NORMAL);
    }
    
    bool StartPropertySetThread::threadLoop() { //实现主要逻辑
        property_set(kTimestampProperty, mTimestampPropertyValue ? "1" : "0");
        property_set("service.bootanim.exit", "0");
        property_set("service.bootanim.progress", "0");
        property_set("ctl.start", "bootanim");
        return false; // 返回fanlse表示 执行一次完后,线程就会等待退出。
    }
    } // namespace android
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    本文相对简单,强调了几个重要函数及其实现。总结一下

    总结一下

    • Android framework层大量使用的线程类为Thread。但是不知是何原因,作者不推荐使用。
    • Thread类其实是对POSIX中pthread线程的一个包装,增强了其功能。
    • 在FrameWork中,Thread类,并不被直接使用。通常的做法是通过继承的方式,子类应该重写必要的函数。重要的函数有:
      • onFirstRef() :继承子RefBase,通常在这里初始化一些变量,并不限于线程。
      • readyToRun() :线程第一次运行时会调用的函数,通常在这里初始化一些线程相关的变量或数据。
      • threadLoop() :线程函数,在其中实现主要的线程逻辑,函数返回true时,还会重新运行,直到函数返回false,或者requestExit()类型的函数被调用
      • requestExit():线程被动退出函数,由其它线程调用,调用后线程可能不会立刻退出。
    • Android Framework中,继承Thread类,实现threadLoop()函数(其中包含主要线程逻辑)。需要创建和启动线程时,调用父类的run()函数即可实现最简单的线程。
  • 相关阅读:
    neo4j 图数据库初步调研 图数据库与关系型数据库差异-f
    如何在 Protocol Buffers (Proto) 文件中弃用一个字段
    动静图结合——冒泡排序
    深入解析:微软Edge浏览器的防钓鱼与恶意软件保护机制
    (十四)STM32——外部中断(EXTI)
    何时使用Elasticsearch而不是MySql
    linux分区扩容
    805.数组的均值分割(回溯+折半搜索+数学)
    华硕X555YI, Win11下无法调节屏幕亮度
    Jmeter获取Websocket多帧消息的实现方法
  • 原文地址:https://blog.csdn.net/qq_25333681/article/details/127947192