Android thread class & threadloop

背景

本周在调试过程中,发现了一个神奇的线程函数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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值