環境: flutter sdk v1.5.4-hotfix.1@stablelinux
對應 flutter engine: 52c7a1e849a170be4b2b2fe34142ca2c0a6fea1fandroid
這裏關注的是flutter在C++層的線程表示, 沒有涉及dart層的線程git
flutter底層(C++)的線程(fml::Thread
)是和消息循環緊密關聯的,即每個fml::Thead
實例都建立了一個消息循環實例,所以若是要建立一個裸線程是不該該用fml::Thread
的。fml::Thread
內部便是用C++11的std::thread
來持有一個線程對象,參看fml::Thread
構造函數(thread.cc:25)。github
線程運行體作了2件事shell
fml::Thread
對象TaskRunner
對象實例並賦值給線程fml::Thread
,即線程也持有一個TaskRunner
實例這個TaskRunner
是個幹啥的,還得看的fml::MessageLoop
實現 fml::Thread
的實現很是簡單,關鍵仍是看它關聯的fml::MessageLoop
。windows
消息循環fml::MessageLoop
首先用了線程存儲來保存一個回調,這個回調的做用是顯式釋放一個fml::MessageLoop
內存對象,因此先搞清flutter底層是如何進行線程存儲的。安全
線程存儲對象即做用域與線程生命週期一致的存儲對象,fml::ThreadLocal
即爲線程存儲類,它要保存的值是一個類型爲intptr_t
的對象; fml::ThreadLocal
在不一樣平臺用了不一樣的實現方式bash
pthread_key_create
來生成一個標識線程的key鍵,key對應的值是一個輔助類Box
,它保存了intptr_t
對象和傳入的回調方法ThreadLocalDestroyCallback
。ThreadLocal
使用前須要聲明的關鍵字是static
對象析構的順序稍有點繞, 各對象析構調用序列以下:ThreadLocal::~ThreadLocal()
ThreadLocal::Box::~Box()
pthread_key_delete(_key)
ThreadLocal::ThreadLocalDestroy
ThreadLocal::Box::DestroyValue
ThreadLocalDestroyCallback() => [](intptr_t value) {}
MessageLoop::~MessageLoop()
ThreadLocal::Box::~Box()
複製代碼
這樣看彷佛thread_local.cc:27
處的delete
操做是多餘的?異步
ThreadLocal
使用前直接用了C++11標準的關鍵字thread_local
。消息循環即異步處理模型,在沒有消息時阻塞當前線程以節省CPU消耗,不然以輪詢的方式空轉很浪費CPU資源,消息循環在安卓平臺上很常見,其實全部的消息循環都大同小異。函數
明白了線程存儲,那麼在建立fml::Thread
對象時調用的MessageLoop::EnsureInitializedForCurrentThread
就很淺顯了(名字雖然有點累贅),當前線程是否建立了消息循環對象,若是沒有那麼建立並保存。這樣消息循環就與線程關聯起來了, 經過什麼關聯的?tls_message_loop
這個線程存儲類對象。
MessageLoopImpl ::delayed_tasks_
就是實際的消息隊列,它被delayed_tasks_mutex_
這個互斥變量保證線程安全。看着有點累贅,其實就是用了一個優先級隊列按執行時間點來插入,若是時間點相同就按FIFO的規則來插入。
隊列元素是一個內部類DelayedTask
, 主要包含消息執行體task
和執行時間點target_time
,order
實際上是用來排序的。
MessageLoop
對象構造函數建立了2個重要實例,消息循環實現體MessageLoopImpl
和fml::TaskRunner
, 而fml::TaskRunner
內部又引用了MessageLoopImpl
。MessageLoopImpl::Create()
建立了不一樣平臺對應的消息循環實現體,因而MessageLoop
與MessageLoopImpl
之間的關係也很是清楚了: MessageLoop
是MessageLoopImpl
的殼或者MessageLoopImpl
是MessageLoop
的代理,MessageLoopImpl
是不對外暴露的、與平臺相關的、真正實現消息讀取與處理的對象。
MessageLoopImpl::Run,Terminate,WakeUp
是純虛函數,由平臺實現,譬如安卓平臺的實現MessageLoopAndroid
調用的是AndroidNDK方法ALooper_pollOnce
, MessageLoopLinux
調用是Linux阻塞函數epoll_wait
。
這裏涉及的類和方法有點繞,其實想達到目的很簡單:讀取並處理消息的操做是統一的,但線程喚醒或者阻塞的方式是容許平臺差別的
一個消息循環關聯一個TaskRunner
,而TaskRunner
細看實現發現全都是MessageLoopImpl
的方法,再聯繫以前在AndroidShellHolder
構造函數裏建立的TaskHost
,就能夠發現所謂的TaskRunner
無非就是給指定消息循環發送消息,而一個消息循環是和一個線程(fml::Thread
)關聯的,於是也也就是給指定線程發送消息,沒錯,正是線程間通訊!TaskRunner
也正是聲明成了線程安全對象(fml::RefCountedThreadSafe<TaskRunner>
)
這樣其實一切都串聯起來了: fml::TaskRunner
正如android中的android.os.Handler
, fml::closure
正如android中的Runnable
, fml::TaskRunner
不斷的將各類fml::closure
對象添加到消息隊列當中,並設定消息循環在指定的時間點喚醒並執行。
fml::Thread
析構函數調用了自身的Join
方法, 這個操做初看有點彆扭,後來才明白意圖:主調線程須要同步的等待被調線程結束,名稱不如Exit
來的言簡意賅。Join
方法先異步發送了一個結束消息循環的請求(MessageLoop::GetCurrent().Terminate()
),而後阻塞式等待結束。 結合以上列出線程退出的調用序列:
Thread::~Thread()
Thread::Join()
TaskRunner::PostTask()
...[異步]
MessageLoop::Terminate()
MessageLoopImpl::DoTerminate()
MessageLoopImpl::Terminate() => MessageLoopAndroid::Terminate()
ALooper_wake()
...[異步,函數開始返回]
MessageLoopImpl::Run() => MessageLoopAndroid::Run()
MessageLoopImpl::RunExpiredTasksNow()
MessageLoopImpl::DoRun()
MessageLoop::Run()
...[異步]
ThreadLocal::~ThreadLocal()
[省略,同線程存儲對象析構的調用序列]
複製代碼
回看AndroidShellHolder
的構造函數,其中涉及flutter::ThreadHost
, fml::TaskRunner
, flutter::TaskRunners
,在建立Shell
對象以前還建立了一系列線程:ui_thread
, gpu_thread
, io_thread
,並對TaskRunner
有一系列操做,有點雜亂但如今看其實就很是清晰了。
當前執行AndroidShellHolder
構造函數的線程被建立了一個消息循環(android_shell_holder.cc:81)並將消息循環的TaskRunner
賦值給了platform_runner
(注意:並無建立platform_thread
對象)。其它的TaskRunner
則分別是所建立的fml::Thread
線程的TaskRunner
對象。
那麼問題來了:當某個線程經過platform_runner
發送一個異步請求時,會在什麼時機執行?