背景
本周在调试过程中,发现了一个神奇的线程函数threadLoop,找遍模块内部所有的代码,居然没有找到该函数是在何处拉起的,仅仅知道这是个循环线程,但想要解决问题,必须知道线程是何时被拉起的,带着这样的疑问我开始了对threadloop的调查。
简述
threadloop顾名思义是一个循环线程,只要返回值为true,则线程就会反复运行,而该方法的调用关系就藏在thread class里面。对于thread class这个类,我的个人理解是将线程创建跟管理的部分抽象出来,既是方便管理,也是在提高代码的复用率。而使用者在自己的派生类中继承thread class,并实现父类中的虚函数(threadloop就是其中一个),即可实现对线程的管理,下面我们举实例说明thread class的大致调用关系及应用方法。
thread class成员
注释太长不贴了,源码位置:/system/core/libutils/include/utils/Thread.h
class Thread : virtual public RefBase
{
public:
explicit Thread(bool canCallJava = true);
virtual ~Thread();
virtual status_t run( const char* name,
int32_t priority = PRIORITY_DEFAULT,
size_t stack = 0);
virtual void requestExit();
virtual status_t readyToRun();
status_t requestExitAndWait();
status_t join();
bool isRunning() const;
#if defined(__ANDROID__)
pid_t getTid() const;
#endif
protected:
bool exitPending() const;
private:
virtual bool threadLoop() = 0;
private:
Thread& operator=(const Thread&);
static int _threadLoop(void* user);
const bool mCanCallJava;
thread_id_t mThread;
mutable Mutex mLock;
Condition mThreadExitedCondition;
status_t mStatus;
volatile bool mExitPending;
volatile bool mRunning;
sp<Thread> mHoldSelf;
#if defined(__ANDROID__)
pid_t mTid;
#endif
};
应用实例
我们以Android中bootanimation为例,源码位置:/frameworks/base/cmds/bootanimation/
1.在头文件中我们可以看到继承关系
class BootAnimation : public Thread, public IBinder::DeathRecipient
2.在onFirestRef函数中,调用了run()方法,有关函数onFirstRef()的调用关系可查阅资料,此处不做过多描述
void BootAnimation::onFirstRef() {
status_t err = mSession->linkToComposerDeath(this);
ALOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));
if (err == NO_ERROR) {
run("BootAnimation", PRIORITY_DISPLAY);
}
}
3.run()方法的实现在父类thread class中,代码路径:/system/core/libutils/Threads.cpp
status_t Thread::run(const char* name, int32_t priority, size_t stack)
{
...
if (mCanCallJava) {
res = createThreadEtc(_threadLoop,
this, name, priority, stack, &mThread);
} else {
res = androidCreateRawThreadEtc(_threadLoop,
this, name, priority, stack, &mThread);
}
...
return NO_ERROR;
// Exiting scope of mLock is a memory barrier and allows new thread to run
}
截取其中创建线程的关键代码,最终调用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_t thread;
int result = pthread_create(&thread, &attr,
(android_pthread_entry)entryFunction, userData);
pthread_attr_destroy(&attr);
if (result != 0) {
ALOGE("androidCreateRawThreadEtc failed (entry=%p, res=%d, %s)\n"
"(android threadPriority=%d)",
entryFunction, result, strerror(errno), threadPriority);
return 0;
}
...
return 1;
}
在这段代码中我们看到了非常熟知的函数pthread_create(),而线程函数是在上一段代码中的_threadLoop()
4._threadLoop()线程被创建后,来到了非常核心的部分
int Thread::_threadLoop(void* user)
{
Thread* const self = static_cast<Thread*>(user);
sp<Thread> strong(self->mHoldSelf);
wp<Thread> weak(strong);
self->mHoldSelf.clear();
#if defined(__ANDROID__)
// this is very useful for debugging with gdb
self->mTid = gettid();
#endif
bool first = true;
do {
bool result;
if (first) {
first = false;
self->mStatus = self->readyToRun();
result = (self->mStatus == NO_ERROR);
if (result && !self->exitPending()) {
// Binder threads (and maybe others) rely on threadLoop
// running at least once after a successful ::readyToRun()
// (unless, of course, the thread has already been asked to exit
// at that point).
// This is because threads are essentially used like this:
// (new ThreadSubclass())->run();
// The caller therefore does not retain a strong reference to
// the thread and the thread would simply disappear after the
// successful ::readyToRun() call instead of entering the
// threadLoop at least once.
result = self->threadLoop();
}
} else {
result = self->threadLoop();
}
// establish a scope for mLock
{
Mutex::Autolock _l(self->mLock);
if (result == false || self->mExitPending) {
self->mExitPending = true;
self->mRunning = false;
// clear thread ID so that requestExitAndWait() does not exit if
// called by a new thread using the same thread ID as this one.
self->mThread = thread_id_t(-1);
// note that interested observers blocked in requestExitAndWait are
// awoken by broadcast, but blocked on mLock until break exits scope
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.我们看到了while循环,这是threadloop能够被反复调用的原因;
2.flag first,意义在于进入循环第一次后会调用方法readyToRun(),这个是父类thread class虚函数,需要子类实现,一般用于初始化动作,为后续循环线程做准备;
3.readyToRun()方法调用返回后,如返回值无误,则直接调用threadLoop(),跟readyToRun()一样,是父类thread class虚函数,同样需要子类实现,这也是我最终想要搞清楚的地方;
4.first过后,再次进入循环时走else分支,直接调用threadLoop(),如返回值为false,则break循环线程退出。
补充说明
至此threadloop调用关系已完全透明,循环线程的应用场景一般是用来处理队列消息,需要一直循环监听,线程内部配合一个阻塞的监听函数,有消息上来就从监听函数返回,处理一下带出来的数据,处理完毕进入下一个循环。
参考链接:https://blog.csdn.net/ch853199769/article/details/79917188