Android thread class & threadloop

spongebob1912發表於2020-12-09

背景

本週在除錯過程中,發現了一個神奇的執行緒函式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

相關文章