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 对象有两种使用方法:
- loop:如果 threadLoop() 返回 true,则在 requestExit() 未被调用时再次调用。
- 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() 函数。
- loop:如果 threadLoop() 返回 true,则在 requestExit() 未被调用时再次调用。
- 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;
}