Android 源码 Native Thread 分析

Native Thread 是架设在 Pthread 基础上为了方便 Native 开发使用的。关于 Pthread 的基础学习请移步《Linux Pthread 常用函数学习与使用》。

下面是 Native Thread 的头文件,注释非常详细了。

/system/core/include/utils/Thread.h

#ifndef _LIBS_UTILS_THREAD_H
#define _LIBS_UTILS_THREAD_H

#include <stdint.h>
#include <sys/types.h>
#include <time.h>

#if !defined(_WIN32)
# include <pthread.h>
#endif

#include <utils/Condition.h>
#include <utils/Errors.h>
#include <utils/Mutex.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
#include <utils/ThreadDefs.h>

// ---------------------------------------------------------------------------
namespace android {
// ---------------------------------------------------------------------------

class Thread : virtual public RefBase
{
public:
    // 创建 Thread 对象,但不创建或启动关联的线程。参见 run() 方法。
                        Thread(bool canCallJava = true);
    virtual             ~Thread();

    // 启动线程
    virtual status_t    run(    const char* name = 0,
                                int32_t priority = PRIORITY_DEFAULT,
                                size_t stack = 0);

    // 请求该对象的线程退出。这个函数是异步的,当函数返回时,线程可能仍在运行。
    // 当然,这个函数可以从不同的线程调用。
    virtual void        requestExit();

    // 这是进行一次性初始化的好地方
    virtual status_t    readyToRun();

    // 调用 requestExit() 并等待,直到该对象的线程退出。要非常小心死锁。
            status_t    requestExitAndWait();

    // 等待直到该对象的线程退出。如果尚未运行,则立即返回。 
    // 不要从该对象的线程中调用;在这种情况下将返回 WOULD_BLOCK。
    // 特别是,从该对象的线程中调用此函数将很愚蠢。在这种情况下将返回 WOULD_BLOCK。
            status_t    join();

    // 指示此线程是否正在运行。
            bool        isRunning() const;

#ifdef HAVE_ANDROID_OS
    // 返回线程的内核 ID,与调用 gettid() 的线程本身相同,如果线程没有运行,则返回 -1。
            pid_t       getTid() const;
#endif

protected:
    // 如果调用了 requestExit(), exitPending() 返回 true。
            bool        exitPending() const;

private:
    // 派生类必须实现 threadLoop(),这是一个虚方法
    virtual bool        threadLoop() = 0;

private:
    Thread& operator=(const Thread&);
    static  int             _threadLoop(void* user);
    const   bool            mCanCallJava;
    // 读或写时总是持有 mLock
            thread_id_t     mThread;
    mutable Mutex           mLock;
            Condition       mThreadExitedCondition;
            status_t        mStatus;
    // 请注意,所有对 mExitPending 和 mRunning 的访问都需要持有 mLock
    volatile bool           mExitPending;
    volatile bool           mRunning;
            sp<Thread>      mHoldSelf;
#ifdef HAVE_ANDROID_OS
    // 用于调试
            pid_t           mTid;
#endif
};


}; // namespace android

// ---------------------------------------------------------------------------
#endif // _LIBS_UTILS_THREAD_H
// ---------------------------------------------------------------------------

Thread 对象的 run 启动线程,并开始运行。

readyToRun() 是一个虚方法需要派生类实现,可以做一些初始化准备工作。

派生类必须实现 threadLoop() 方法,Thread 对象有两种使用方法:

  1. loop:如果 threadLoop() 返回 true,则在 requestExit() 未被调用时再次调用。
  2. once:如果 threadLoop() 返回 false,则该线程将在返回时退出。

requestExit() 请求退出线程,立即返回。requestExitAndWait() 方法的唯一不同是先调用 requestExit() 请求退出线程,然后接着等待退出,不立即返回。

join() 会等待线程退出。

其他几个方法获取一些线程状态和属性。

一、run 函数分析

run 方法中首先判断 mRunning 标志,如果线程已经在运行,则马上返回 INVALID_OPERATION。接着给一些变量赋予默认值,最后通过调用 createThreadEtc 或 androidCreateRawThreadEtc 方法创建 Pthread 线程。如果创建 Pthread 线程发生错误,则重置一些变量。

/system/core/libutils/Threads.cpp

status_t Thread::run(const char* name, int32_t priority, size_t stack)
{
    Mutex::Autolock _l(mLock);

    if (mRunning) {
        // 线程已经启动
        return INVALID_OPERATION;
    }

    mStatus = NO_ERROR;
    mExitPending = false;
    mThread = thread_id_t(-1);

    // 对自身持有强引用
    mHoldSelf = this;

    mRunning = true;

    bool res;
    if (mCanCallJava) {
        res = createThreadEtc(_threadLoop,
                this, name, priority, stack, &mThread);
    } else {
        res = androidCreateRawThreadEtc(_threadLoop,
                this, name, priority, stack, &mThread);
    }

    if (res == false) {
        mStatus = UNKNOWN_ERROR;   // 未知错误
        mRunning = false;
        mThread = thread_id_t(-1);
        mHoldSelf.clear();

        return UNKNOWN_ERROR;
    }
    
    return NO_ERROR;
}

假如 mCanCallJava 为 true,接下来就会调用 createThreadEtc 函数。传递到次函数的参数包括 _threadLoop 函数指针、Thread 对象自身、名称、优先级、栈大小和 mThread 指针(用于返回线程 ID)。

/system/core/include/utils/AndroidThreads.h

inline bool createThreadEtc(thread_func_t entryFunction,
                            void *userData,
                            const char* threadName = "android:unnamed_thread",
                            int32_t threadPriority = PRIORITY_DEFAULT,
                            size_t threadStackSize = 0,
                            thread_id_t *threadId = 0)
{
    return androidCreateThreadEtc(entryFunction, userData, threadName,
        threadPriority, threadStackSize, threadId) ? true : false;
}

createThreadEtc 函数没干什么,马上调用了 androidCreateThreadEtc 来完成实际工作。

/system/core/libutils/Threads.cpp

int androidCreateThreadEtc(android_thread_func_t entryFunction,
                            void *userData,
                            const char* threadName,
                            int32_t threadPriority,
                            size_t threadStackSize,
                            android_thread_id_t *threadId)
{
    return gCreateThreadFn(entryFunction, userData, threadName,
        threadPriority, threadStackSize, threadId);
}

gCreateThreadFn 是一个全局变量,它是一个函数指针,类型定义为 android_create_thread_fn。

/system/core/libutils/Threads.cpp

static android_create_thread_fn gCreateThreadFn = androidCreateRawThreadEtc;

androidCreateRawThreadEtc 函数才是真正完成线程创建的函数。它使用了 Pthread 中的 pthread_create 函数完成真正的线程创建。

/system/core/libutils/Threads.cpp

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);

#ifdef HAVE_ANDROID_OS
    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;
        // 当需要使用 nice/setpriority 设置优先级和使用 prctl 设置名称时,我们使用这个 trampoline
        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);
    // result 不等于 0 表示创建失败
    if (result != 0) {
        ALOGE("androidCreateRawThreadEtc failed (entry=%p, res=%d, errno=%d)\n"
             "(android threadPriority=%d)",
            entryFunction, result, errno, threadPriority);
        return 0;
    }
    // 设置线程 ID
    if (threadId != NULL) {
        *threadId = (android_thread_id_t)thread;
    }
    return 1;
}

下面是 thread_data_t 结构体,其中定义了 trampoline 函数,此函数中设置了优先级和线程名称。

/system/core/libutils/Threads.cpp

struct thread_data_t {
    thread_func_t   entryFunction;
    void*           userData;
    int             priority;
    char *          threadName;
    
    static int trampoline(const thread_data_t* t) {
        thread_func_t f = t->entryFunction;
        void* u = t->userData;
        int prio = t->priority;
        char * name = t->threadName;
        delete t;
        setpriority(PRIO_PROCESS, 0, prio);
        if (prio >= ANDROID_PRIORITY_BACKGROUND) {
            set_sched_policy(0, SP_BACKGROUND);
        } else {
            set_sched_policy(0, SP_FOREGROUND);
        }

        if (name) {
            androidSetThreadName(name);
            free(name);
        }
        return f(u);
    }
};

知识点:

在任何一个时间点上,线程是可结合的(joinable),或者是分离的(detached)。一个可结合的线程能够被其他线程收回其资源和杀死;在被其他线程回收之前,它的存储器资源(如栈)是不释放的。相反,一个分离的线程是不能被其他线程回收或杀死的,它的存储器资源在它终止时由系统自动释放。

线程的分离状态决定一个线程以什么样的方式来终止自己。在默认情况下线程是非分离状态的,这种情况下,原有的线程等待创建的线程结束。只有当 pthread_join() 函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。而分离线程不是这样子的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。所以如果我们在创建线程时就知道不需要了解线程的终止状态,则可以 pthread_attr_t 结构中的 detachstate 线程属性,让线程以分离状态启动。

设置线程分离状态的函数为pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)。第二个参数可选为 PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD _CREATE_JOINABLE(非分离线程)。这里要注意的一点是,如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在 pthread_create 函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用 pthread_create 的线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用 pthread_cond_timewait 函数,让这个线程等待一会儿,留出足够的时间让函数 pthread_create 返回。设置一段等待时间,是在多线程编程里常用的方法。但是注意不要使用诸如 wait() 之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。

二、_threadLoop 函数分析

线程启动以后,最终会调用 _threadLoop 函数。

/system/core/libutils/Threads.cpp

int Thread::_threadLoop(void* user)
{
    Thread* const self = static_cast<Thread*>(user);
    // 持有强引用
    sp<Thread> strong(self->mHoldSelf);
    wp<Thread> weak(strong);
    self->mHoldSelf.clear();

#ifdef HAVE_ANDROID_OS
    // 这对于使用 gdb 进行调试非常有用
    self->mTid = gettid();
#endif

    bool first = true;

    do {
        bool result;
        if (first) {
            first = false;
            // 调用 readyToRun() 做一些准备工作
            self->mStatus = self->readyToRun();
            result = (self->mStatus == NO_ERROR);

            if (result && !self->exitPending()) {
                // 首次运行
                result = self->threadLoop();
            }
        } else {
            // threadLoop() 返回 true,以后每次这里调用
            result = self->threadLoop();
        }

        // 为 mLock 建立一个作用域
        {
        Mutex::Autolock _l(self->mLock);
        if (result == false || self->mExitPending) {
            self->mExitPending = true;
            self->mRunning = false;
            // 清除线程ID,以便在新线程使用与当前线程相同的线程 ID 调用 requestExitAndWait() 时不退出。
            self->mThread = thread_id_t(-1);
            // 注意,在 requestExitAndWait 中阻塞的感兴趣的观察者被广播唤醒,但在 mLock 上阻塞,直到 break 退出作用域
            self->mThreadExitedCondition.broadcast();
            break;
        }
        }

        // 释放强引用,以便线程有机会平静的死亡
        strong.clear();
        // 并且立即为下一个循环重新获取一个强引用
        strong = weak.promote();
    } while(strong != 0);

    return 0;
}

源码中可以看出 threadLoop() 返回结果至关重要,关乎只运行一次还是循环运行 threadLoop() 函数。

  1. loop:如果 threadLoop() 返回 true,则在 requestExit() 未被调用时再次调用。
  2. once:如果 threadLoop() 返回 false,则该线程将在返回时退出。

三、requestExitAndWait 函数分析

如果从 Thread 对象的线程中调用,通过判断线程 ID 是否相等来判定是否为同一线程,在这种情况下返回 WOULD_BLOCK。如果不是同一线程中调用 requestExitAndWait 函数,则先将 mExitPending 赋值为 true,表示将要退出线程运行。接着判断 mRunning 标志,如果为 true,说明需要等待 threadLoop() 运行结束,接着 _threadLoop 函数进入 mLock 作用域。requestExitAndWait 函数中调用线程则在 mLock 上等待,当 _threadLoop 函数中赋值 mRunning 为 false 以后,调用 mThreadExitedCondition 对象的 broadcast() 唤醒阻塞线程,也就会唤醒调用 requestExitAndWait 函数的线程,继续运行运行 requestExitAndWait 函数剩余代码。

/system/core/libutils/Threads.cpp

status_t Thread::requestExitAndWait()
{
    Mutex::Autolock _l(mLock);
    if (mThread == getThreadId()) {
        ALOGW(
        "Thread (this=%p): don't call waitForExit() from this "
        "Thread object's thread. It's a guaranteed deadlock!",
        this);

        return WOULD_BLOCK;
    }

    mExitPending = true;

    while (mRunning == true) {
        mThreadExitedCondition.wait(mLock);
    }
    // 下一行可能不再需要,但留作历史参考。请注意,每个相关方将清除标志。
    mExitPending = false;

    return mStatus;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TYYJ-洪伟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值