內容
- 概述
- 快速入門指南
- 核心概念
- 線程詞典
- 線程
- 任務
- 優先使用序列而不是物理線程
- 發布并行任務
- 直接發布到線程池
- 通過 TaskRunner 發布
- 發布順序任務
- 發布到新序列
- 發布到當前(虛擬)主題
- 使用序列代替鎖
- 將多個任務發布到同一線程
- 發布到瀏覽器進程中的主線程或 IO 線程
- 發送到渲染進程的主線程
- 發布到自定義 SingleThreadTaskRunner
- 發帖至當前主題
- 將任務發布到 COM 單線程單元 (STA) 線程 (Windows)
- 已發布任務的內存排序保證
- 使用 TaskTraits 注釋任務
- 保持瀏覽器響應
- 延遲發布任務
- 延遲發布一次性任務
- 延遲發布重復任務
- 取消任務
- 使用 base::WeakPtr
- 使用 base::CancelableTaskTracker
- 發布并行運行的作業
- 向正在運行的作業添加額外工作
- 測試
- 在新進程中使用線程池
- TaskRunner 所有權(鼓勵不依賴注入)
- 常問問題
- 內部
- 序列管理器
- 消息泵
- 運行循環
- 任務重入
- 通用 API
- SingleThreadTaskExecutor 和 TaskEnvironment
- MessageLoop 和 MessageLoopCurrent
注意:有關更多示例,請參閱線程和任務常見問題解答。
概述
Chrome 采用多進程架構,每個進程都高度多線程化。本文檔將介紹每個進程共享的基本線程系統。我們的主要目標是保持瀏覽器的高響應速度。在沒有外部延遲或工作負載要求的情況下,Chrome 致力于成為一個高并發系統,但不一定是并行系統。
關于 Chromium 并發(尤其是序列)方式的基本介紹可以在這里找到。
本文檔假設您熟悉計算機科學線程概念。
快速入門指南
- 不要在主線程(即瀏覽器進程中的“UI”線程)或 IO 線程(每個進程用于接收 IPC 的線程)上執行昂貴的計算或阻塞 IO。繁忙的 UI / IO 線程可能會導致用戶可見的延遲,因此最好將這些工作放在線程池中運行。
- 始終避免從不同的線程或序列讀取/寫入內存中的同一位置。這會導致數據爭用!建議跨序列傳遞消息。不建議使用鎖等消息傳遞的替代方案。
- 如果您需要協調存在于不同序列中的多個對象,請注意對象的生命周期。
- 為了防止意外的數據爭用,大多數類最好只用于單個序列。您應該使用SEQUENCE_CHECKER或base::SequenceBound等實用程序來幫助強制執行此約束。
- 根據經驗法則,避免使用base::Unretained。弱指針通常可以被替代。
std::unique_ptr
最好通過以下方式明確所有權。- scoped_refptrs可用于跨多個序列擁有多個所有者的對象。這通常是錯誤的設計模式,不建議在新代碼中使用。
核心概念
- 任務:待處理的工作單元。實際上是一個帶有可選關聯狀態的函數指針。在 Chrome 中,它
base::OnceCallback
分別通過和 來base::RepeatingCallback
創建。(文檔)。base::BindOnce
base::BindRepeating
- 任務隊列:待處理的任務隊列。
- 物理線程:操作系統提供的線程(例如 POSIX 上的 pthread 或 Windows 上的 CreateThread())。Chrome 的跨平臺抽象是
base::PlatformThread
。您幾乎不應該直接使用它。 base::Thread
:一個物理線程永遠處理來自專用任務隊列的消息,直到 Quit() 為止。您幾乎不應該創建自己的base::Thread
線程。- 線程池:一個包含共享任務隊列的物理線程池。在 Chrome 中,線程池就是一個線程池
base::ThreadPoolInstance
。每個 Chrome 進程只有一個線程池實例,用于處理通過線程池發送的任務base/task/thread_pool.h,因此您很少需要base::ThreadPoolInstance
直接使用 API(稍后會詳細介紹發送任務)。 - 序列線程或虛擬線程:Chrome 管理的執行線程。與物理線程類似,任何時刻,給定序列/虛擬線程上只能運行一個任務,并且每個任務都會受到前一個任務的影響。任務按順序執行,但可以在各個任務之間跳過物理線程。
- 任務運行器:用于發布任務的界面。在 Chrome 中,它是
base::TaskRunner
。 - 順序任務運行器:一種任務運行器,它保證提交的任務按提交順序依次運行。每個此類任務都保證能夠看到其前一個任務的副作用。提交到順序任務運行器的任務通常由單個線程(虛擬線程或物理線程)處理。在 Chrome 中,這就是
base::SequencedTaskRunner
which is-a?base::TaskRunner
。 - 單線程任務運行器:一種順序任務運行器,保證所有任務都由同一個物理線程處理。在 Chrome 中,它就是
base::SingleThreadTaskRunner
which is-a?base::SequencedTaskRunner
。我們盡可能優先使用序列而不是線程。
線程詞典
讀者須知:以下術語旨在彌合常見線程命名法與我們在 Chrome 中使用它們的方式之間的差距。如果您剛開始學習,可能會覺得有點難懂。如果難以理解,請考慮跳至下文更詳細的部分,并在必要時再參考本部分。
- 線程不安全:Chrome 中的絕大多數類型都是線程不安全的(設計如此)。訪問此類類型/方法必須進行外部同步。通常,線程不安全的類型要求所有訪問其狀態的任務都發布到同一個線程
base::SequencedTaskRunner
,并在調試版本中使用成員進行驗證SEQUENCE_CHECKER
。鎖也是一種同步訪問的選項,但在 Chrome 中,我們強烈建議使用序列而不是鎖。 - 線程親和性:此類類型/方法需要始終從同一物理線程(即從同一個
base::SingleThreadTaskRunner
)訪問,并且通常有一個THREAD_CHECKER
成員來驗證它們是否屬于線程親和性。除非使用第三方 API 或具有線程親和性的葉子依賴項,否則在 Chrome 中,幾乎沒有理由讓類型成為線程親和性。請注意,base::SingleThreadTaskRunner
is-abase::SequencedTaskRunner
類型是線程不安全的子集。線程親和性有時也稱為線程敵對性。 - 線程安全:此類類型/方法可以安全地并行訪問。
- 線程兼容:此類類型提供對 const 方法的安全并行訪問,但對非常量(或混合 const/非常量訪問)需要同步。Chrome 不提供讀寫鎖;因此,唯一的用例是對象(通常是全局變量),它們以線程安全的方式初始化一次(可以在啟動的單線程階段初始化,也可以通過線程安全的靜態局部初始化范式延遲初始化
base::NoDestructor
),并且此后永遠不可變。 - 不可變:線程兼容類型的子集,構造后無法修改。
- 序列友好:此類類型/方法是線程非安全類型,支持從 調用
base::SequencedTaskRunner
。理想情況下,所有線程非安全類型都應該如此,但遺留代碼有時會在單純的線程非安全場景中強制執行線程親和性檢查,從而過度嚴格。有關更多詳細信息,請參閱下文的“優先使用序列而非線程” 。
線程
每個 Chrome 進程都有
- 主線程
- 在瀏覽器進程中(BrowserThread::UI):更新UI
- 在渲染器進程(Blink 主線程)中:運行大部分 Blink 功能
- IO線程
- 在所有進程中:所有 IPC 消息都到達此線程。處理該消息的應用程序邏輯可能位于不同的線程中(例如,IO 線程可能會將消息路由到綁定到不同線程的Mojo 接口)。
- 更一般地,大多數異步 I/O 發生在這個線程上(例如,通過 base::FileDescriptorWatcher)。
- 在瀏覽器進程中:這被稱為BrowserThread::IO。
- 一些特殊用途的線程
- 以及一個通用線程池
大多數線程都有一個循環,從隊列中獲取任務并運行它們(該隊列可能由多個線程共享)。
任務
任務被base::OnceClosure
添加到隊列中以便異步執行。
Abase::OnceClosure
存儲一個函數指針和參數。它有一個Run()
方法,可以使用綁定的參數調用該函數指針。它是使用 創建的base::BindOnce
。(參考Callback<> 和 Bind() 文檔)。
<span style="background-color:#fafafa"><span style="color:#000000">無效任務A(){}
無效 TaskB(int v){}自動任務_a = base::BindOnce(&TaskA);
自動任務b = base::BindOnce(&TaskB,42);
</span></span>
一組任務可以通過以下方式之一執行:
- 并行:沒有任務執行順序,可能在任何線程上同時執行
- 順序:按發布順序執行任務,在任何線程上一次執行一個任務。
- 單線程:按發布順序執行任務,一次在單個線程上執行一個任務。
- COM 單線程:COM 初始化的單線程變體。
優先使用序列而不是物理線程
強烈建議在虛擬線程上進行順序執行,而不是在物理線程上進行單線程執行。除了綁定到主線程 (UI) 或 IO 線程的類型/方法外,base::SequencedTaskRunner
通過管理自己的物理線程(參見下文的“發布順序任務”)可以更好地實現線程安全。
為“當前物理線程”公開的所有 API 都具有與“當前序列”等效的 API(映射)。
如果您發現自己編寫了一個序列友好的類型,但它THREAD_CHECKER
在葉子依賴項中未通過線程親和性檢查(例如),請考慮將該依賴項也設置為序列友好的。Chrome 中的大多數核心 API 都是序列友好的,但某些遺留類型可能仍然過度使用 ThreadChecker/SingleThreadTaskRunner,而實際上它們可以依賴于“當前序列”,不再是線程親和性的。
發布并行任務
直接發布到線程池
可以運行在任何線程上并且與其他任務沒有排序或互斥要求的任務應該使用base::ThreadPool::PostTask*()
中定義的函數之一來發布base/task/thread_pool.h。
<span style="background-color:#fafafa"><span style="color:#000000"><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">ThreadPool </span><span style="color:#666600">:: </span><span style="color:#660066">PostTask </span><span style="color:#666600">(</span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">,</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(& </span><span style="color:#660066">Task </span><span style="color:#666600">));</span>
</span></span>
這會發布具有默認特征的任務。
這些base::ThreadPool::PostTask*()
函數允許調用者通過 TaskTraits 提供有關任務的更多詳細信息(參考使用 TaskTraits 注釋任務)。
<span style="background-color:#fafafa"><span style="color:#000000"><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">ThreadPool </span><span style="color:#666600">:: </span><span style="color:#660066">PostTask </span><span style="color:#666600">(</span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">,</span><span style="color:#666600">{ </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">TaskPriority </span><span style="color:#666600">:: </span><span style="color:#000000">BEST_EFFORT </span><span style="color:#666600">,</span><span style="color:#660066">MayBlock </span><span style="color:#666600">()},</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(& </span><span style="color:#660066">Task </span><span style="color:#666600">));</span>
</span></span>
通過 TaskRunner 發布
并行base::TaskRunner是直接調用的替代方案base::ThreadPool::PostTask*()
。這主要適用于無法預先知道任務是并行、順序還是單線程發送的情況(參見發送順序任務、將多個任務發送到同一線程)。由于是和base::TaskRunner
的基類,因此成員可以包含、或。base::SequencedTaskRunner
base::SingleThreadTaskRunner
scoped_refptr<TaskRunner>
base::TaskRunner
base::SequencedTaskRunner
base::SingleThreadTaskRunner
<span style="background-color:#fafafa"><span style="color:#000000"><span style="color:#000088">類</span><span style="color:#000000">A </span><span style="color:#666600">{ </span><span style="color:#000088">public </span><span style="color:#666600">: </span><span style="color:#000000">A </span><span style="color:#666600">() </span><span style="color:#666600">= </span><span style="color:#000088">default </span><span style="color:#666600">;</span><span style="color:#000088">void </span><span style="color:#660066">PostSomething </span><span style="color:#666600">() </span><span style="color:#666600">{ </span><span style="color:#000000">task_runner_ </span><span style="color:#666600">-> </span><span style="color:#660066">PostTask </span><span style="color:#666600">( </span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">,</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(& </span><span style="color:#000000">A </span><span style="color:#666600">,</span><span style="color:#666600">& </span><span style="color:#660066">DoSomething </span><span style="color:#666600">)); </span><span style="color:#666600">}</span> <span style="color:#000088">無效的</span><span style="color:#660066">執行某事</span><span style="color:#666600">()</span><span style="color:#666600">{ </span><span style="color:#666600">}</span> <span style="color:#000088">私有</span><span style="color:#666600">:</span><span style="color:#000000">scoped_refptr </span><span style="color:#666600">< </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">TaskRunner </span><span style="color:#666600">> </span><span style="color:#000000">task_runner_ </span><span style="color:#666600">= </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">ThreadPool </span><span style="color:#666600">:: </span><span style="color:#660066">CreateTaskRunner </span><span style="color:#666600">({ </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">TaskPriority </span><span style="color:#666600">:: </span><span style="color:#000000">USER_VISIBLE </span><span style="color:#666600">}); </span><span style="color:#666600">};</span></span></span>
除非測試需要精確控制任務的執行方式,否則最好base::ThreadPool::PostTask*()
直接調用(參考測試以了解在測試中以較少侵入的方式控制任務)。
發布順序任務
序列是一組按發布順序逐個運行的任務(不一定在同一線程中)。要將任務發布為序列的一部分,請使用base::SequencedTaskRunner。
發布到新序列
base::SequencedTaskRunner
可以通過 創建A?base::ThreadPool::CreateSequencedTaskRunner()
。
<span style="background-color:#fafafa"><span style="color:#000000"><span style="color:#000000">scoped_refptr </span><span style="color:#666600"><SequencedTaskRunner> </span><span style="color:#660066">sequenced_task_runner </span><span style="color:#000000">= </span><span style="color:#666600">base </span><span style="color:#000000">:: </span><span style="color:#666600">ThreadPool </span><span style="color:#666600">:: </span><span style="color:#660066">CreateSequencedTaskRunner </span><span style="color:#666600">(... </span><span style="color:#666600">)</span><span style="color:#660066">;</span><span style="color:#880000">// TaskB 在 TaskA 完成后運行。</span><span style="color:#000000">
sequenced_task_runner </span><span style="color:#666600">-> </span><span style="color:#660066">PostTask </span><span style="color:#666600">( </span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">, </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(& </span><span style="color:#660066">TaskA </span><span style="color:#666600">)); </span><span style="color:#000000">
sequenced_task_runner </span><span style="color:#666600">-> </span><span style="color:#660066">PostTask </span><span style="color:#666600">( </span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">, </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(& </span><span style="color:#660066">TaskB </span><span style="color:#666600">));</span>
</span></span>
發布到當前(虛擬)主題
發布到當前(虛擬)線程的首選方式是通過base::SequencedTaskRunner::GetCurrentDefault()
。
<span style="background-color:#fafafa"><span style="color:#000000"><span style="color:#880000">// 該任務將在當前(虛擬)線程的默認任務隊列上運行。base </span><span style="color:#000000">
:: </span><span style="color:#660066">SequencedTaskRunner </span><span style="color:#666600">:: </span><span style="color:#660066">GetCurrentDefault </span><span style="color:#666600">()-> </span><span style="color:#660066">PostTask </span><span style="color:#666600">( </span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">, </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">( </span><span style="color:#666600">& </span><span style="color:#660066">Task </span><span style="color:#666600">));</span>
</span></span>
請注意,SequencedTaskRunner::GetCurrentDefault()
返回的是當前虛擬線程的默認隊列。在具有多個任務隊列的線程(例如 BrowserThread::UI)中,該隊列可能與當前任務所屬的隊列不同。“當前”任務運行器有意不通過靜態 getter 公開。您要么已經知道它并可以直接向其發送消息,要么不知道,唯一合理的目的地是默認隊列。有關詳細討論,請參閱https://bit.ly/3JvCLsX 。
使用序列代替鎖
Chrome 不鼓勵使用鎖。序列本身就提供了線程安全。建議您使用始終從同一序列訪問的類,而不是使用鎖來管理您自己的線程安全。
線程安全但不線程親和;這是為什么呢?發送到同一序列的任務將按順序運行。一個已排序的任務完成后,下一個任務可能會被另一個工作線程執行,但該任務肯定會受到前一個任務對其序列造成的任何副作用的影響。
<span style="background-color:#fafafa"><span style="color:#000000"><span style="color:#000088">class </span><span style="color:#000000">A </span><span style="color:#666600">{ </span><span style="color:#000088">public </span><span style="color:#666600">: </span><span style="color:#000000">A </span><span style="color:#666600">() </span><span style="color:#666600">{ </span><span style="color:#880000">// 不需要訪問創建序列。DETACH_FROM_SEQUENCE </span><span style="color:#666600">( </span><span style="color:#000000">sequence_checker_ </span><span style="color:#000000">) </span><span style="color:#666600">; </span><span style="color:#666600">}</span><span style="color:#000088">void </span><span style="color:#660066">AddValue </span><span style="color:#666600">( </span><span style="color:#660066">int </span><span style="color:#000000">v </span><span style="color:#666600">) </span><span style="color:#666600">{ </span><span style="color:#880000">// 檢查所有訪問是否在同一個序列上。DCHECK_CALLED_ON_VALID_SEQUENCE </span><span style="color:#666600">( </span><span style="color:#000000">sequence_checker_ </span><span style="color:#666600">); </span><span style="color:#000000">values_ </span><span style="color:#666600">. </span><span style="color:#000000">push_back </span><span style="color:#666600">( </span><span style="color:#000000">v </span><span style="color:#000000">) </span><span style="color:#666600">; </span><span style="color:#666600">}</span> <span style="color:#000088">私有</span><span style="color:#666600">:</span><span style="color:#000000">SEQUENCE_CHECKER </span><span style="color:#666600">(</span><span style="color:#000000">sequence_checker_ </span><span style="color:#666600">);</span><span style="color:#880000">// 不需要鎖,因為所有訪問都在 // 同一個序列上</span><span style="color:#666600">。std </span><span style="color:#880000">:: </span><span style="color:#000000">vector </span><span style="color:#660066"><int> </span><span style="color:#008800">values_ </span><span style="color:#000000">; </span><span style="color:#666600">} </span><span style="color:#666600">;</span><span style="color:#000000">A a </span><span style="color:#666600">; </span><span style="color:#000000">
scoped_refptr </span><span style="color:#666600">< </span><span style="color:#660066">SequencedTaskRunner </span><span style="color:#666600">> </span><span style="color:#000000">task_runner_for_a </span><span style="color:#666600">= </span><span style="color:#666600">...; </span><span style="color:#000000">
task_runner_for_a </span><span style="color:#666600">-> </span><span style="color:#660066">PostTask </span><span style="color:#666600">(</span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">,</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(& </span><span style="color:#000000">A </span><span style="color:#666600">:: </span><span style="color:#660066">AddValue </span><span style="color:#666600">,</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">Unretained </span><span style="color:#666600">(&a </span><span style="color:#000000">a </span><span style="color:#666600">), </span><span style="color:#006666">42 </span><span style="color:#666600">)); </span><span style="color:#000000">
task_runner_for_a </span><span style="color:#666600">-> </span><span style="color:#660066">PostTask </span><span style="color:#666600">( </span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">, </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(& </span><span style="color:#000000">A </span><span style="color:#666600">:: </span><span style="color:#660066">AddValue </span><span style="color:#666600">, </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">Unretained </span><span style="color:#666600">(&a </span><span style="color:#000000">a </span><span style="color:#666600">), </span><span style="color:#006666">27 </span><span style="color:#666600">));</span> <span style="color:#880000">// 從不同的序列訪問會導致 DCHECK 失敗。scoped_refptr </span><span style="color:#666600"><SequencedTaskRunner> </span><span style="color:#660066">other_task_runner </span><span style="color:#666600">= </span><span style="color:#666600">...; </span><span style="color:#000000">other_task_runner </span><span style="color:#000000">
- </span><span style="color:#666600">> </span><span style="color:#000000">
PostTask </span><span style="color:#660066">( </span><span style="color:#666600">FROM_HERE </span><span style="color:#000000">, </span><span style="color:#666600">base </span><span style="color:#000000">:: </span><span style="color:#666600">BindOnce </span><span style="color:#660066">( </span><span style="color:#666600">& </span><span style="color:#000000">A </span><span style="color:#666600">:: </span><span style="color:#666600">AddValue </span><span style="color:#660066">, </span><span style="color:#666600">base </span><span style="color:#000000">:: </span><span style="color:#666600">Unretained </span><span style="color:#660066">( </span><span style="color:#666600">&a </span><span style="color:#000000">a </span><span style="color:#666600">), </span><span style="color:#006666">1 </span><span style="color:#666600">));</span>
</span></span>
鎖應該僅用于交換可在多個線程上訪問的共享數據結構。如果一個線程基于昂貴的計算或通過磁盤訪問來更新該結構,那么這項緩慢的工作應該在不持有鎖的情況下完成。只有當結果可用時,才應該使用鎖來交換新數據。PluginList::LoadPlugins ( ) 中就有一個這樣的示例content/browser/plugin_list.cc。如果必須使用鎖,以下是一些最佳實踐和需要避免的陷阱。
為了編寫非阻塞代碼,Chrome 中的許多 API 都是異步的。通常,這意味著它們要么需要在特定的線程/序列上執行,并通過自定義委托接口返回結果,要么接受一個base::OnceCallback<>
(或base::RepeatingCallback<>
) 對象,該對象在請求的操作完成時被調用。上文 PostTask 部分介紹了如何在特定的線程/序列上執行工作。
將多個任務發布到同一線程
如果多個任務需要在同一個線程上運行,請將它們發布到base::SingleThreadTaskRunner。發布到同一個線程的所有任務將按照base::SingleThreadTaskRunner
發布順序在同一個線程上運行。
發布到瀏覽器進程中的主線程或 IO 線程
要將任務發布到主線程或 IO 線程,請使用content::GetUIThreadTaskRunner({})
或content::GetIOThreadTaskRunner({})
從content/public/browser/browser_thread.h
您可以向這些方法提供額外的 BrowserTaskTraits 作為參數,盡管這在 BrowserThreads 中通常并不常見,應該保留用于高級用例。
目前正在進行從以前的 base-API-with-traits 的遷移(任務 API v3 ),您可能仍會在整個代碼庫中找到它(它是等效的):
<span style="background-color:#fafafa"><span style="color:#000000"><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">PostTask </span><span style="color:#666600">(</span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">,</span><span style="color:#666600">{</span><span style="color:#000000">內容</span><span style="color:#666600">::</span><span style="color:#660066">瀏覽器線程</span><span style="color:#666600">:: </span><span style="color:#000000">UI </span><span style="color:#666600">},</span><span style="color:#666600">...);</span> <span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">CreateSingleThreadTaskRunner </span><span style="color:#666600">({ </span><span style="color:#000000">content </span><span style="color:#666600">:: </span><span style="color:#660066">BrowserThread </span><span style="color:#666600">:: </span><span style="color:#000000">IO </span><span style="color:#666600">})</span><span style="color:#666600">-> </span><span style="color:#660066">PostTask </span><span style="color:#666600">(</span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">,</span><span style="color:#666600">...);</span></span></span>
注意:在遷移期間,很遺憾,您需要繼續手動包含content/public/browser/browser_task_traits.h. 以使用 browser_thread.h API。
主線程和 IO 線程已經非常繁忙。因此,盡可能將任務發布到通用線程(參見發布并行任務、發布順序任務)。將任務發布到主線程的良好理由是更新 UI 或訪問綁定到主線程的對象(例如Profile
)。將任務發布到 IO 線程的良好理由是訪問綁定到主線程的組件內部(例如 IPC、網絡)。注意:無需顯式地將任務發布到 IO 線程即可發送/接收 IPC 或在網絡上發送/接收數據。
發送到渲染進程的主線程
TODO(blink-dev)
發布到自定義 SingleThreadTaskRunner
如果多個任務需要在同一個線程上運行,并且該線程不必是主線程或 IO 線程,則將它們發布到base::SingleThreadTaskRunner
由...創建的base::Threadpool::CreateSingleThreadTaskRunner
。
<span style="background-color:#fafafa"><span style="color:#000000"><span style="color:#000000">scoped_refptr </span><span style="color:#666600"><SingleThreadTaskRunner> </span><span style="color:#660066">single_thread_task_runner </span><span style="color:#666600">= </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#666600">Threadpool </span><span style="color:#666600">:: </span><span style="color:#660066">CreateSingleThreadTaskRunner </span><span style="color:#666600">(... </span><span style="color:#660066">)</span><span style="color:#000000">;</span><span style="color:#880000">// TaskB 在 TaskA 完成后運行。兩個任務在同一個線程上運行。single_thread_task_runner </span><span style="color:#666600">-> </span><span style="color:#660066">PostTask </span><span style="color:#666600">( </span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">, </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(& </span><span style="color:#660066">TaskA </span><span style="color:#666600">)); </span><span style="color:#000000">
single_thread_task_runner </span><span style="color:#666600">-> </span><span style="color:#660066">PostTask </span><span style="color:#666600">( </span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">, </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(& </span><span style="color:#660066">TaskB </span><span style="color:#666600">)) </span><span style="color:#000000">
;</span>
</span></span>
請記住,我們更喜歡序列而不是物理線程,因此這很少是必要的。
發帖至當前主題
base::SequencedTaskRunner::GetCurrentDefault()
而不是base::SingleThreadTaskRunner::GetCurrentDefault()
(請參閱發布到當前序列)。這樣可以更好地記錄發布任務的要求,并避免不必要地使 API 依賴于物理線程。在單線程任務中,base::SequencedTaskRunner::GetCurrentDefault()
相當于base::SingleThreadTaskRunner::GetCurrentDefault()
。
如果您仍然必須將任務發布到當前物理線程,請使用base::SingleThreadTaskRunner::CurrentDefaultHandle。
<span style="background-color:#fafafa"><span style="color:#000000"><span style="color:#000000">
// 該任務</span><span style="color:#880000">將來會在當前線程上運行。base </span><span style="color:#666600">:: </span><span style="color:#660066">SingleThreadTaskRunner </span><span style="color:#666600">:: </span><span style="color:#660066">GetCurrentDefault </span><span style="color:#666600">()-> </span><span style="color:#660066">PostTask </span><span style="color:#666600">( </span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">, </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(& </span><span style="color:#660066">Task </span><span style="color:#666600">));</span>
</span></span>
將任務發布到 COM 單線程單元 (STA) 線程 (Windows)
需要在 COM 單線程單元 (STA) 線程上運行的任務必須發布到base::SingleThreadTaskRunner
返回的base::ThreadPool::CreateCOMSTATaskRunner()
。如將多個任務發布到同一線程中所述,所有發布到同一線程的任務均按base::SingleThreadTaskRunner
發布順序在同一線程上運行。
<span style="background-color:#fafafa"><span style="color:#000000"><span style="color:#880000">// Task(A|B|C)UsingCOMSTA 將在同一個 COM STA 線程上運行。</span><span style="color:#000088">void </span><span style="color:#660066">TaskAUingCOMSTA </span><span style="color:#666600">() </span><span style="color:#666600">{ </span><span style="color:#880000">// [這在 COM STA 線程上運行。]</span> <span style="color:#880000">// 進行 COM STA 調用。</span><span style="color:#880000">// ...</span><span style="color:#666600">// 將另一個</span><span style="color:#880000">任務發布到當前 COM STA 線程。base </span><span style="color:#666600">:: </span><span style="color:#660066">SingleThreadTaskRunner </span><span style="color:#666600">:: </span><span style="color:#660066">GetCurrentDefault </span><span style="color:#666600">()-> </span><span style="color:#660066">PostTask </span><span style="color:#666600">( </span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">, </span><span style="color:#000000">base </span><span style="color:#000000">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(& </span><span style="color:#660066">TaskCUsingCOMSTA </span><span style="color:#666600">)); </span><span style="color:#666600">} </span><span style="color:#000088">void </span><span style="color:#660066">TaskBUsingCOMSTA </span><span style="color:#666600">() </span><span style="color:#666600">{ </span><span style="color:#666600">} </span><span style="color:#000088">void </span><span style="color:#660066">TaskCUsingCOMSTA </span><span style="color:#666600">() </span><span style="color:#666600">{ </span><span style="color:#666600">}</span><span style="color:#000088">自動</span><span style="color:#000000">com_sta_task_runner </span><span style="color:#666600">= </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">ThreadPool </span><span style="color:#666600">:: </span><span style="color:#660066">CreateCOMSTATaskRunner </span><span style="color:#666600">(...); </span><span style="color:#000000">
com_sta_task_runner </span><span style="color:#666600">-> </span><span style="color:#660066">PostTask </span><span style="color:#666600">(</span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">,</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(&</span><span style="color:#660066">TaskAUsingCOMSTA </span><span style="color:#666600">)); </span><span style="color:#000000">
com_sta_task_runner </span><span style="color:#666600">-> </span><span style="color:#660066">PostTask </span><span style="color:#666600">(</span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">,</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(&</span><span style="color:#660066">TaskBUsingCOMSTA </span><span style="color:#666600">));</span>
</span></span>
已發布任務的內存排序保證
此任務系統保證在任務開始運行時,所有在發布任務之前順序執行的內存效應都對任務可見PostTask()
。更正式地說,對的調用和發布任務的執行彼此之間存在先行發生關系::base
。這適用于所有以 方式發布任務的變體,包括PostTaskAndReply()
。同樣,對于作為同一 SequencedTaskRunner 的一部分按順序運行的任務,也存在先行發生關系。
了解這一保證至關重要,因為 Chrome 任務通常會訪問復制到 中的直接數據以外的內存base::OnceCallback
,而這種先行關系可以避免任務本身內部的額外同步。舉一個非常具體的例子,考慮一個回調,它將一個指針綁定到剛剛在發布任務的線程中初始化的內存上。
一個更受約束的模型也值得注意。執行可以拆分成多個任務,分別運行在不同的任務運行器上,每個任務都以獨占方式訪問內存中的特定對象,無需顯式同步。發布另一個任務會將(對象的)“所有權”轉移給下一個任務。這樣,對象所有權的概念通常可以擴展到任務運行器的級別,從而提供有用的不變量來推理。該模型可以避免競爭條件,同時避免鎖和原子操作。由于其簡單性,該模型在 Chrome 中被廣泛使用。
使用 TaskTraits 注釋任務
base::TaskTraits封裝有關任務的信息,幫助線程池做出更好的調度決策。
base::TaskTraits
當默認特征足夠時,可以傳遞需要的方法{}
。默認特征適用于以下任務:
- 不要阻塞(參考 MayBlock 和 WithBaseSyncPrimitives);
- 與用戶阻止活動有關;(通過與執行該操作的組件建立排序依賴關系來明確或隱含地表示)
- 可以阻止關機或在關機時跳過(線程池可以自由選擇合適的默認值)。
與該描述不匹配的任務必須使用明確的 TaskTraits 進行發布。
base/task/task_traits.h提供所有可用特征的詳盡文檔。內容層還提供了其他特征,content/public/browser/browser_task_traits.h以便于將任務發布到瀏覽器線程 (BrowserThread)。
下面是一些如何指定的示例base::TaskTraits
。
<span style="background-color:#fafafa"><span style="color:#000000"><span style="color:#880000">// 此任務沒有顯式的 TaskTraits。它無法阻塞。其優先級為</span><span style="color:#880000">// USER_BLOCKING。它將阻塞關閉或在關閉時被跳過。base </span><span style="color:#660066">:: ThreadPool </span><span style="color:#666600">:: </span><span style="color:#000000">
PostTask </span><span style="color:#666600">( </span><span style="color:#660066">FROM_HERE </span><span style="color:#666600">, </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#000000">BindOnce </span><span style="color:#666600">( </span><span style="color:#660066">... </span><span style="color:#666600">));</span><span style="color:#880000">// 此任務具有最高優先級。線程池將在</span><span style="color:#880000">USER_VISIBLE 和 BEST_EFFORT 任務之前調度它。base </span><span style="color:#666600">:: </span><span style="color:#660066">ThreadPool </span><span style="color:#666600">:: </span><span style="color:#660066">PostTask </span><span style="color:#666600">( </span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">, </span><span style="color:#666600">{ </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">TaskPriority </span><span style="color:#000000">
:: </span><span style="color:#000000">USER_BLOCKING </span><span style="color:#666600">} </span><span style="color:#666600">, </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(...));</span><span style="color:#880000">// 此任務具有最低優先級,并允許阻塞(例如,它</span><span style="color:#880000">//可以從磁盤讀取文件)。base </span><span style="color:#666600">:: </span><span style="color:#660066">ThreadPool </span><span style="color:#666600">:: </span><span style="color:#660066">PostTask </span><span style="color:#666600">(</span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">,</span><span style="color:#666600">{ </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">TaskPriority </span><span style="color:#000000">
:: </span><span style="color:#000000">BEST_EFFORT </span><span style="color:#666600">,</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">MayBlock </span><span style="color:#666600">()},</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(</span><span style="color:#666600">...));</span><span style="color:#880000">// 此任務會阻止關閉。進程在其執行完成</span><span style="color:#880000">之前不會退出。base </span><span style="color:#666600">:: </span><span style="color:#660066">ThreadPool </span><span style="color:#666600">:: </span><span style="color:#660066">PostTask </span><span style="color:#666600">( </span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">, </span><span style="color:#666600">{ </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">TaskShutdownBehavior </span><span style="color:#000000">
:: </span><span style="color:#000000">BLOCK_SHUTDOWN </span><span style="color:#666600">} </span><span style="color:#666600">, </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(...));</span></span></span>
保持瀏覽器響應
不要在主線程、IO 線程或任何預期以低延遲運行任務的序列上執行高開銷工作。相反,請使用base::ThreadPool::PostTaskAndReply*()
或異步執行高開銷工作base::SequencedTaskRunner::PostTaskAndReply()
。請注意,IO 線程上的異步/重疊 I/O 是可以的。
示例:在主線程上運行下面的代碼將導致瀏覽器長時間無法響應用戶輸入。
<span style="background-color:#fafafa"><span style="color:#000000"><span style="color:#880000">// GetHistoryItemsFromDisk() 可能會阻塞很長時間。 // AddHistoryItemsToOmniboxDropDown( </span><span style="color:#660066">)</span><span style="color:#880000">更新 UI,因此必須</span><span style="color:#880000">// 在主線程上調用。AddHistoryItemsToOmniboxDropdown </span><span style="color:#666600">( </span><span style="color:#660066">GetHistoryItemsFromDisk </span><span style="color:#666600">( </span><span style="color:#008800">"keyword" </span><span style="color:#666600">));</span></span></span>
下面的代碼解決了這個問題,它先在線程池中調用 ,GetHistoryItemsFromDisk()
然后在原始序列(本例中是主線程)中調用AddHistoryItemsToOmniboxDropdown()
。第一次調用的返回值會自動作為第二次調用的參數。
<span style="background-color:#fafafa"><span style="color:#000000"><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">ThreadPool </span><span style="color:#666600">:: </span><span style="color:#660066">PostTaskAndReplyWithResult </span><span style="color:#666600">(</span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">,</span><span style="color:#666600">{ </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">MayBlock </span><span style="color:#666600">()},</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(& </span><span style="color:#660066">GetHistoryItemsFromDisk </span><span style="color:#666600">,</span><span style="color:#008800">“關鍵字” </span><span style="color:#666600">),</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(& </span><span style="color:#660066">AddHistoryItemsToOmniboxDropdown </span><span style="color:#666600">));</span>
</span></span>
延遲發布任務
延遲發布一次性任務
要發布延遲到期后必須運行一次的任務,請使用base::ThreadPool::PostDelayedTask*()
或base::TaskRunner::PostDelayedTask()
。
<span style="background-color:#fafafa"><span style="color:#000000"><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">ThreadPool </span><span style="color:#666600">:: </span><span style="color:#660066">PostDelayedTask </span><span style="color:#666600">(</span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">,</span><span style="color:#666600">{ </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">TaskPriority </span><span style="color:#666600">:: </span><span style="color:#000000">BEST_EFFORT </span><span style="color:#666600">},</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(& </span><span style="color:#660066">Task </span><span style="color:#666600">),</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">Hours </span><span style="color:#666600">(</span><span style="color:#006666">1 </span><span style="color:#666600">));</span> <span style="color:#000000">scoped_refptr </span><span style="color:#666600">< </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">SequencedTaskRunner </span><span style="color:#666600">> </span><span style="color:#000000">task_runner </span><span style="color:#666600">= </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">ThreadPool </span><span style="color:#666600">:: </span><span style="color:#660066">CreateSequencedTaskRunner </span><span style="color:#666600">(</span><span style="color:#666600">{ </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">TaskPriority </span><span style="color:#666600">:: </span><span style="color:#000000">BEST_EFFORT </span><span style="color:#666600">}); </span><span style="color:#000000">
task_runner </span><span style="color:#666600">-> </span><span style="color:#660066">PostDelayedTask </span><span style="color:#666600">(</span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">,</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(& </span><span style="color:#660066">Task </span><span style="color:#666600">),</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">Hours </span><span style="color:#666600">(</span><span style="color:#006666">1 </span><span style="color:#666600">));</span></span></span>
base::TaskPriority::BEST_EFFORT
以防止其在延遲到期時拖慢瀏覽器速度。
延遲發布重復任務
要發布必須定期運行的任務,請使用base::RepeatingTimer。
<span style="background-color:#fafafa"><span style="color:#000000"><span style="color:#000088">class </span><span style="color:#000000">A </span><span style="color:#666600">{ </span><span style="color:#000088">public </span><span style="color:#666600">: </span><span style="color:#666600">~ </span><span style="color:#000000">A </span><span style="color:#666600">() </span><span style="color:#666600">{ </span><span style="color:#880000">// 計時器在刪除時自動停止。</span><span style="color:#666600">} </span><span style="color:#000088">void </span><span style="color:#660066">StartDoingStuff </span><span style="color:#666600">() </span><span style="color:#666600">{ </span><span style="color:#000000">timer_ </span><span style="color:#666600">. </span><span style="color:#660066">Start </span><span style="color:#666600">( </span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">, </span><span style="color:#660066">Seconds </span><span style="color:#666600">( </span><span style="color:#006666">1 </span><span style="color:#666600">), </span><span style="color:#000088">this </span><span style="color:#666600">, </span><span style="color:#666600">& </span><span style="color:#000000">A </span><span style="color:#666600">:: </span><span style="color:#660066">DoStuff </span><span style="color:#666600">); </span><span style="color:#666600">} </span><span style="color:#000088">void </span><span style="color:#660066">StopDoingStuff </span><span style="color:#666600">() </span><span style="color:#666600">{ </span><span style="color:#000000">timer_ </span><span style="color:#666600">. </span><span style="color:#660066">Stop </span><span style="color:#666600">(); </span><span style="color:#666600">} </span><span style="color:#000088">private </span><span style="color:#666600">: </span><span style="color:#000088">void </span><span style="color:#660066">DoStuff </span><span style="color:#666600">() </span><span style="color:#666600">{ </span><span style="color:#880000">// 在調用 StartDoingStuff() 的序列中每秒調用此方法</span><span style="color:#880000">。</span><span style="color:#666600">} </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">RepeatingTimer </span><span style="color:#000000">timer_ </span><span style="color:#666600">; </span><span style="color:#666600">};</span></span></span>
取消任務
使用 base::WeakPtr
base::WeakPtr可用于確保在對象被銷毀時取消綁定到該對象的任何回調。
<span style="background-color:#fafafa"><span style="color:#000000"><span style="color:#660066">int</span><span style="color:#660066">計算</span><span style="color:#666600">() </span><span style="color:#666600">{ </span><span style="color:#666600">… </span><span style="color:#666600">}</span> <span style="color:#000088">class </span><span style="color:#000000">A </span><span style="color:#666600">{ </span><span style="color:#000088">public </span><span style="color:#666600">: </span><span style="color:#000088">void </span><span style="color:#660066">ComputeAndStore </span><span style="color:#666600">() </span><span style="color:#666600">{ </span><span style="color:#880000">// 在線程池中安排對 Compute() 的調用,然后</span><span style="color:#880000">在當前序列中調用 A::Store()。對 // A::Store() 的調用</span><span style="color:#880000">在 |weak_ptr_factory_| 被銷毀時被取消。</span><span style="color:#880000">// (保證 |this| 不會被釋放后使用)。base </span><span style="color:#666600">:: </span><span style="color:#000000">ThreadPool </span><span style="color:#666600">:: </span><span style="color:#660066">PostTaskAndReplyWithResult </span><span style="color:#660066">( </span><span style="color:#666600">FROM_HERE </span><span style="color:#000000">, </span><span style="color:#666600">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(& </span><span style="color:#660066">Compute </span><span style="color:#666600">), </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#000000">( </span><span style="color:#666600">& </span><span style="color:#000000">A </span><span style="color:#666600">:: </span><span style="color:#660066">Store </span><span style="color:#666600">, </span><span style="color:#000000">weak_ptr_factory_ </span><span style="color:#666600">. </span><span style="color:#660066">GetWeakPtr </span><span style="color:#666600">())); </span><span style="color:#666600">}</span><span style="color:#000088">私有</span><span style="color:#666600">:</span><span style="color:#000088">void</span><span style="color:#660066">存儲</span><span style="color:#666600">(</span><span style="color:#660066">int</span><span style="color:#000000">值</span><span style="color:#666600">)</span><span style="color:#666600">{ </span><span style="color:#000000">value_ </span><span style="color:#666600">= </span><span style="color:#000000">value </span><span style="color:#666600">; </span><span style="color:#666600">}</span><span style="color:#660066">int </span><span style="color:#000000">value_ </span><span style="color:#666600">; </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">WeakPtrFactory </span><span style="color:#666600"><A> </span><span style="color:#666600">weak_ptr_factory_ </span><span style="color:#000000">{ </span><span style="color:#000000">this </span><span style="color:#000088">} </span><span style="color:#666600">; </span><span style="color:#666600">} </span><span style="color:#666600">;</span></span></span>
注意:WeakPtr
不是線程安全的:~WeakPtrFactory()
并且Store()
(綁定到WeakPtr
)必須全部在同一個序列上運行。
使用 base::CancelableTaskTracker
base::CancelableTaskTracker允許以與任務運行順序不同的順序取消任務。請記住,CancelableTaskTracker
無法取消已經開始運行的任務。
<span style="background-color:#fafafa"><span style="color:#000000"><span style="color:#000088">auto </span><span style="color:#000000">task_runner </span><span style="color:#666600">= </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">ThreadPool </span><span style="color:#666600">:: </span><span style="color:#660066">CreateTaskRunner </span><span style="color:#666600">({}); </span><span style="color:#000000">
base </span><span style="color:#666600">:: </span><span style="color:#660066">CancelableTaskTracker </span><span style="color:#000000">cancellationable_task_tracker </span><span style="color:#666600">; </span><span style="color:#000000">
cancellationable_task_tracker.PostTask </span><span style="color:#666600">( </span><span style="color:#660066">task_runner.get </span><span style="color:#666600">(), </span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">, </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">DoNothing </span><span style="color:#666600">()); // 僅</span><span style="color:#660066">當Task( </span><span style="color:#000000">)</span><span style="color:#880000">尚未開始運行時才</span><span style="color:#000000">取消</span><span style="color:#000000">
它</span><span style="color:#666600">。cancelable_task_tracker.TryCancelAll </span><span style="color:#666600">( </span><span style="color:#666600">) </span><span style="color:#666600">;</span></span></span>
發布并行運行的作業
這base::PostJob是一個高級用戶 API,能夠調度單個 base::RepeatingCallback 工作任務,并請求 ThreadPool 工作線程并行調用該任務。這可以避免以下情況:
- 調用
PostTask()
每個工作項,造成大量開銷。 - 固定數量的
PostTask()
調用會拆分工作,并且可能運行很長時間。當許多組件發布“核心數”任務,并且所有組件都希望使用所有核心時,這會帶來問題。在這種情況下,調度程序缺乏上下文,無法公平地處理多個相同優先級的請求,并且/或者無法在高優先級工作到來時請求低優先級工作讓步。
請參閱base/task/job_perftest.cc完整的示例。
<span style="background-color:#fafafa"><span style="color:#000000"><span style="color:#880000">// |worker_task| 的規范實現。void </span><span style="color:#000088">WorkerTask </span><span style="color:#666600">( </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">JobDelegate </span><span style="color:#666600">* </span><span style="color:#000000">job_delegate </span><span style="color:#666600">) </span><span style="color:#666600">{ </span><span style="color:#000088">while </span><span style="color:#666600">(! </span><span style="color:#000000">job_delegate </span><span style="color:#666600">-> </span><span style="color:#660066">ShouldYield </span><span style="color:#666600">()) </span><span style="color:#666600">{ </span><span style="color:#000088">auto </span><span style="color:#000000">work_item </span><span style="color:#666600">= </span><span style="color:#660066">TakeWorkItem </span><span style="color:#666600">(); </span><span style="color:#880000">// 最小工作單位。if </span><span style="color:#000088">( </span><span style="color:#666600">! </span><span style="color:#000000">work_item </span><span style="color:#666600">) </span><span style="color:#000088">return </span><span style="color:#660066">: </span><span style="color:#666600">ProcessWork </span><span style="color:#666600">( </span><span style="color:#000000">work_item </span><span style="color:#660066">) </span><span style="color:#666600">; </span><span style="color:#666600">} </span><span style="color:#666600">}</span><span style="color:#880000">// 返回最新的線程安全未完成工作項數量。</span><span style="color:#000088">void </span><span style="color:#660066">NumIncompleteWorkItems </span><span style="color:#666600">( </span><span style="color:#660066">size_t </span><span style="color:#000000">worker_count </span><span style="color:#666600">) </span><span style="color:#666600">{ </span><span style="color:#880000">// 如果 NumIncompleteWorkItems() 需要考慮</span><span style="color:#880000">本地工作列表,則可以使用 |worker_count|,這比自己進行核算更容易,同時記住</span><span style="color:#880000">,實際項目數量可能被高估,因此</span><span style="color:#880000">// 當沒有可用的工作時,可能會調用 WorkerTask()。</span><span style="color:#000088">return </span><span style="color:#660066">GlobalQueueSize </span><span style="color:#666600">() </span><span style="color:#666600">+ </span><span style="color:#000000">worker_count </span><span style="color:#666600">; </span><span style="color:#666600">}</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">PostJob </span><span style="color:#666600">(</span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">,</span><span style="color:#666600">{},</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindRepeating </span><span style="color:#666600">(& </span><span style="color:#660066">WorkerTask </span><span style="color:#666600">),</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindRepeating </span><span style="color:#666600">(& </span><span style="color:#660066">NumIncompleteWorkItems </span><span style="color:#666600">));</span>
</span></span>
通過在調用時循環執行盡可能多的工作,工作任務可以避免調度開銷。同時,任務base::JobDelegate::ShouldYield()
會定期調用,以便有條件地退出,并讓調度程序優先處理其他工作。例如,這種讓步語義允許用戶可見的作業使用所有核心,但在用戶阻塞任務進入時讓出。
向正在運行的作業添加額外工作
當添加新的工作項并且 API 用戶希望額外的線程并行調用工作任務時,JobHandle/JobDelegate::NotifyConcurrencyIncrease()
?必須在最大并發數增加后不久調用。
測試
有關更多詳細信息,請參閱測試發布任務的組件。
要測試使用 或 中的函數的代碼base::SingleThreadTaskRunner::CurrentDefaultHandle
,base::SequencedTaskRunner::CurrentDefaultHandle
請在測試范圍內base/task/thread_pool.h實例化。如果需要 BrowserThreads,請使用而不是。base::test::TaskEnvironmentcontent::BrowserTaskEnvironment
base::test::TaskEnvironment
測試可以base::test::TaskEnvironment
使用 運行 的消息泵base::RunLoop
,可以使其運行直到Quit()
(明確或通過RunLoop::QuitClosure()
),或運行到RunUntilIdle()
準備運行的任務并立即返回。
如果在 TestTimeouts::action_timeout() 之后未明確退出,TaskEnvironment 會將 RunLoop::Run() 配置為 GTEST_FAIL()。這比測試代碼未能觸發 RunLoop 退出導致測試掛起要好得多。可以使用 base::test::ScopedRunLoopTimeout 覆蓋超時時間。
<span style="background-color:#fafafa"><span style="color:#000000"><span style="color:#000088">類</span><span style="color:#660066">MyTest </span><span style="color:#666600">:</span><span style="color:#000088">公共</span><span style="color:#000000">測試</span><span style="color:#666600">::</span><span style="color:#660066">測試</span><span style="color:#666600">{</span><span style="color:#000088">公共</span><span style="color:#666600">:</span><span style="color:#880000">//...</span><span style="color:#000088">受保護的</span><span style="color:#666600">:</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#000000">test </span><span style="color:#666600">:: </span><span style="color:#660066">TaskEnvironment </span><span style="color:#000000">task_environment_ </span><span style="color:#666600">; </span><span style="color:#666600">};</span> <span style="color:#000000">TEST_F </span><span style="color:#666600">(</span><span style="color:#660066">MyTest </span><span style="color:#666600">,</span><span style="color:#660066">FirstTest </span><span style="color:#666600">)</span><span style="color:#666600">{ </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">SingleThreadTaskRunner </span><span style="color:#666600">:: </span><span style="color:#660066">GetCurrentDefault </span><span style="color:#666600">()-> </span><span style="color:#660066">PostTask </span><span style="color:#666600">(</span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">,</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(& </span><span style="color:#000000">A </span><span style="color:#666600">));</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">SequencedTaskRunner </span><span style="color:#666600">:: </span><span style="color:#660066">GetCurrentDefault </span><span style="color:#666600">()-> </span><span style="color:#660066">PostTask </span><span style="color:#666600">(</span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">,</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(& </span><span style="color:#000000">B </span><span style="color:#666600">));</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">SingleThreadTaskRunner </span><span style="color:#666600">:: </span><span style="color:#660066">GetCurrentDefault </span><span style="color:#666600">()-> </span><span style="color:#660066">PostDelayedTask </span><span style="color:#666600">(</span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">,</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(& </span><span style="color:#000000">C </span><span style="color:#666600">),</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">TimeDelta </span><span style="color:#666600">:: </span><span style="color:#660066">Max </span><span style="color:#666600">());</span> <span style="color:#880000">// 這將運行 (SingleThread|Sequenced)TaskRunner::CurrentDefaultHandle 隊列,直到它為空。</span><span style="color:#880000">// 延遲的任務只有在執行條件成熟時才會添加到隊列中。</span><span style="color:#880000">// 盡可能優先使用明確的退出條件而不是 RunUntilIdle:</span><span style="color:#880000">// bit.ly/run-until-idle-with-care2.base </span><span style="color:#000000">:: </span><span style="color:#660066">RunLoop </span><span style="color:#666600">(). </span><span style="color:#666600">RunUntilIdle </span><span style="color:#660066">( </span><span style="color:#666600">); </span><span style="color:#880000">// A 和 B 已執行。C 尚未成熟。</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">RunLoop </span><span style="color:#000000">run_loop </span><span style="color:#666600">;</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">SingleThreadTaskRunner </span><span style="color:#666600">:: </span><span style="color:#660066">GetCurrentDefault </span><span style="color:#666600">()-> </span><span style="color:#660066">PostTask </span><span style="color:#666600">(</span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">,</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(& </span><span style="color:#000000">D </span><span style="color:#666600">));</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">SingleThreadTaskRunner </span><span style="color:#666600">:: </span><span style="color:#660066">GetCurrentDefault </span><span style="color:#666600">()-> </span><span style="color:#660066">PostTask </span><span style="color:#666600">(</span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">,</span><span style="color:#000000">run_loop.QuitClosure </span><span style="color:#666600">());</span><span style="color:#660066">base </span><span style="color:#000000">:: </span><span style="color:#666600">SingleThreadTaskRunner </span><span style="color:#660066">:: </span><span style="color:#666600">GetCurrentDefault </span><span style="color:#660066">(</span><span style="color:#666600">)-> </span><span style="color:#660066">PostTask </span><span style="color:#666600">(</span><span style="color:#666600">FROM_HERE </span><span style="color:#666600">,</span><span style="color:#000000">base </span><span style="color:#000000">:: </span><span style="color:#666600">BindOnce </span><span style="color:#666600">(& </span><span style="color:#000000">E </span><span style="color:#666600">))</span><span style="color:#660066">;</span><span style="color:#880000">// 這將運行 (SingleThread|Sequenced)TaskRunner::CurrentDefaultHandle 隊列,直到</span><span style="color:#880000">調用</span><span style="color:#000000">QuitClosure。run_loop.Run </span><span style="color:#666600">(); </span><span style="color:#666600">// D 和 run_loop.QuitClosure() 已執行</span><span style="color:#660066">。E</span><span style="color:#880000">仍在隊列中。</span><span style="color:#880000">// 發布到線程池的任務在發布時異步運行。base </span><span style="color:#666600">:: </span><span style="color:#666600">ThreadPool </span><span style="color:#660066">:: </span><span style="color:#666600">PostTask </span><span style="color:#660066">(</span><span style="color:#666600">FROM_HERE </span><span style="color:#000000">,</span><span style="color:#666600">{ </span><span style="color:#666600">},</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(& </span><span style="color:#000000">F </span><span style="color:#666600">)); </span><span style="color:#000088">auto </span><span style="color:#000000">task_runner </span><span style="color:#000000">= </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">ThreadPool </span><span style="color:#666600">:: </span><span style="color:#660066">CreateSequencedTaskRunner </span><span style="color:#666600">({}); </span><span style="color:#000000">task_runner </span><span style="color:#666600">-> </span><span style="color:#660066">PostTask </span><span style="color:#666600">(</span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">,</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(& </span><span style="color:#000000">G </span><span style="color:#666600">));</span> <span style="color:#880000">// 阻塞直到發布到線程池的所有任務都運行完成:</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">ThreadPoolInstance </span><span style="color:#666600">:: </span><span style="color:#660066">Get </span><span style="color:#666600">()-> </span><span style="color:#660066">FlushForTesting </span><span style="color:#666600">(); </span><span style="color:#880000">// F 和 G 已經執行。</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">ThreadPool </span><span style="color:#666600">:: </span><span style="color:#660066">PostTaskAndReplyWithResult </span><span style="color:#666600">(</span><span style="color:#000000">FROM_HERE </span><span style="color:#666600">,</span><span style="color:#666600">{},</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(&</span><span style="color:#000000">H </span><span style="color:#666600">),</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">BindOnce </span><span style="color:#666600">(&</span><span style="color:#000000">I </span><span style="color:#666600">));</span> <span style="color:#880000">// 這將運行 (SingleThread|Sequenced)TaskRunner::CurrentDefaultHandle 隊列,直到</span><span style="color:#880000">// (SingleThread|Sequenced)TaskRunner::CurrentDefaultHandle 隊列和 ThreadPool 隊列都為</span><span style="color:#880000">// 空。盡可能優先使用明確的退出條件而不是 RunUntilIdle:</span><span style="color:#880000">// bit.ly/run-until-idle-with-care2. </span><span style="color:#000000">task_environment_ </span><span style="color:#666600">. </span><span style="color:#660066">RunUntilIdle </span><span style="color:#666600">(); </span><span style="color:#880000">// E、H、I 已執行。</span><span style="color:#666600">}</span></span></span>
在新進程中使用線程池
在使用ThreadPoolInstance 函數之前,需要在進程中初始化 ThreadPoolInstance。Chromebase/task/thread_pool.h瀏覽器進程及其子進程(渲染器、GPU、實用程序)中的 ThreadPoolInstance 初始化已完成。要在其他進程中使用 ThreadPoolInstance,請在主函數的早期階段初始化 ThreadPoolInstance:
<span style="background-color:#fafafa"><span style="color:#000000"><span style="color:#880000">// 這將使用默認參數初始化并啟動 ThreadPoolInstance。base </span><span style="color:#666600">:: </span><span style="color:#660066">ThreadPoolInstance </span><span style="color:#666600">:: </span><span style="color:#660066">CreateAndStartWithDefaultParams </span><span style="color:#666600">(</span><span style="color:#008800">“process_name” </span><span style="color:#666600">); // </span><span style="color:#000000">
base </span><span style="color:#880000">/task/thread_pool.h API 現在可以與 base::ThreadPool 特征一起使用。//</span><span style="color:#880000">任務將在發布時進行安排。</span><span style="color:#880000">// 這將初始化 ThreadPoolInstance。base </span><span style="color:#000000">
:: </span><span style="color:#660066">ThreadPoolInstance </span><span style="color:#666600">:: </span><span style="color:#660066">Create </span><span style="color:#666600">( </span><span style="color:#008800">"process_name" </span><span style="color:#666600">); </span><span style="color:#880000">// base/task/thread_pool.h API 現在可與 base::ThreadPool 特性一起使用。在調用 Start() 之前,不會</span><span style="color:#666600">創建</span><span style="color:#880000">任何線程,也不會安排任何任務</span><span style="color:#666600">。base :: </span><span style="color:#880000">ThreadPoolInstance </span><span style="color:#000000">
:: </span><span style="color:#660066">Get </span><span style="color:#666600">( </span><span style="color:#660066">) </span><span style="color:#666600">-> </span><span style="color:#660066">Start </span><span style="color:#666600">( </span><span style="color:#000000">params </span><span style="color:#666600">); </span><span style="color:#880000">// ThreadPool 現在可以創建線程并安排任務。</span></span></span>
并在主函數中關閉ThreadPoolInstance:
<span style="background-color:#fafafa"><span style="color:#000000"><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">ThreadPoolInstance </span><span style="color:#666600">:: </span><span style="color:#660066">Get </span><span style="color:#666600">()-> </span><span style="color:#660066">Shutdown </span><span style="color:#666600">(); </span><span style="color:#880000">// 使用 TaskShutdownBehavior::BLOCK_SHUTDOWN 發出的任務和</span><span style="color:#880000">// 使用 TaskShutdownBehavior::SKIP_ON_SHUTDOWN 發出的任務,</span><span style="color:#880000">在 Shutdown() 調用之前已開始運行,現已完成</span><span style="color:#880000">執行。使用 // TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN 發出的任務</span><span style="color:#880000">可能仍在</span><span style="color:#880000">運行。</span></span></span>
TaskRunner 所有權(鼓勵不依賴注入)
TaskRunner 不應該被傳遞到多個組件。相反,應該由使用 TaskRunner 的組件來創建它。
請參閱此重構示例,其中 TaskRunner 被傳遞到多個組件,最終在葉子節點中使用。現在,葉子節點可以并且應該直接從 獲取其 TaskRunner?base/task/thread_pool.h。
如上所述,base::test::TaskEnvironment
允許單元測試控制從底層 TaskRunner 發出的任務。在極少數情況下,測試需要更精確地控制任務順序:TaskRunner 的依賴注入可能會很有用。對于這種情況,首選方法如下:
<span style="background-color:#fafafa"><span style="color:#000000"><span style="color:#000088">類</span><span style="color:#660066">Foo </span><span style="color:#666600">{</span><span style="color:#000088">公共</span><span style="color:#666600">:</span> <span style="color:#880000">// 在測試中覆蓋 |background_task_runner_|。void </span><span style="color:#666600">SetBackgroundTaskRunnerForTesting </span><span style="color:#660066">( </span><span style="color:#666600">scoped_refptr </span><span style="color:#000088">< </span><span style="color:#666600">base </span><span style="color:#000000">:: </span><span style="color:#666600">SequencedTaskRunner </span><span style="color:#660066">> </span><span style="color:#000000">background_task_runner </span><span style="color:#000000">) </span><span style="color:#666600">{ </span><span style="color:#666600">background_task_runner_ </span><span style="color:#000000">= </span><span style="color:#666600">std </span><span style="color:#000000">:: </span><span style="color:#666600">move </span><span style="color:#000000">( </span><span style="color:#666600">background_task_runner </span><span style="color:#000000">) </span><span style="color:#666600">; </span><span style="color:#666600">}</span><span style="color:#000088">私有</span><span style="color:#666600">:</span><span style="color:#000000">scoped_refptr </span><span style="color:#666600">< </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">SequencedTaskRunner </span><span style="color:#666600">> </span><span style="color:#000000">background_task_runner_ </span><span style="color:#666600">= </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">ThreadPool </span><span style="color:#666600">:: </span><span style="color:#660066">CreateSequencedTaskRunner </span><span style="color:#666600">(</span><span style="color:#666600">{ </span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">MayBlock </span><span style="color:#666600">(),</span><span style="color:#000000">base </span><span style="color:#666600">:: </span><span style="color:#660066">TaskPriority </span><span style="color:#666600">:: </span><span style="color:#000000">BEST_EFFORT </span><span style="color:#666600">}); </span><span style="color:#666600">}</span></span></span>
請注意,這仍然允許刪除 //chrome 和該組件之間的所有管道層,因為單元測試將直接使用葉層。
常問問題
有關更多示例,請參閱線程和任務常見問題解答。
內部
序列管理器
SequenceManager管理具有不同屬性(例如優先級、通用任務類型)的任務隊列 (TaskQueue),將所有已發布的任務多路復用到單個后臺序列中。這通常是一個 MessagePump。根據所使用的消息泵類型,其他事件(例如 UI 消息)也可能會被處理。在 Windows 上,APC 調用(如果時間允許)以及發送到已注冊的 HANDLE 集合的信號也可能會被處理。
消息泵
MessagePump負責處理原生消息,并定期將周期分配給其委托(SequenceManager)。MessagePump 會將委托回調與原生消息處理混合使用,確保兩種類型的事件都不會導致對方的周期不足。
有不同的MessagePumpTypes,最常見的是:
-
默認:僅支持任務和計時器
-
UI:支持本機UI事件(例如Windows消息)
-
IO:支持異步IO(不是文件I/O!)
-
自定義:用戶提供的 MessagePump 接口實現
運行循環
RunLoop 是一個輔助類,用于運行與當前線程(通常是 SequenceManager)關聯的 RunLoop::Delegate。在堆棧上創建一個 RunLoop,然后調用 Run/Quit 來運行嵌套的 RunLoop,但請避免在生產代碼中使用嵌套循環!
任務重入
SequenceManager 具有任務重入保護功能。這意味著,如果正在處理一個任務,則第二個任務在第一個任務完成之前無法啟動。重入可以在處理任務時發生,并會創建一個內部消息泵。該內部消息泵隨后處理可以隱式啟動內部任務的原生消息。內部消息泵可以通過對話框 (DialogBox)、通用對話框 (GetOpenFileName)、OLE 函數 (DoDragDrop)、打印機函數 (StartDoc) 以及許多其他函數創建。
<span style="background-color:#fafafa"><span style="color:#000000"><span style="color:#000000">需要內部任務處理時的</span><span style="color:#660066">示例解決方法</span><span style="color:#666600">:</span><span style="color:#000000">HRESULT hr </span><span style="color:#666600">; </span><span style="color:#666600">{ </span><span style="color:#660066">CurrentThread </span><span style="color:#666600">:: </span><span style="color:#660066">ScopedAllowApplicationTasksInNativeNestedLoop </span><span style="color:#000000">allow </span><span style="color:#666600">; </span><span style="color:#000000">hr </span><span style="color:#666600">= </span><span style="color:#660066">DoDragDrop </span><span style="color:#666600">(...); </span><span style="color:#880000">//隱式運行模態消息循環。</span><span style="color:#666600">} </span><span style="color:#880000">//處理|hr|(DoDragDrop()返回的結果)。</span></span></span>
在使用 CurrentThread::ScopedAllowApplicationTasksInNativeNestedLoop 之前,請確保您的任務是可重入的(可嵌套的),并且所有全局變量都是穩定且可訪問的。
通用 API
用戶代碼幾乎不需要直接訪問 SequenceManager API,因為它們是為處理調度的代碼而設計的。您應該使用以下代碼:
-
base::RunLoop:從綁定的線程驅動 SequenceManager。
-
base::Thread/SequencedTaskRunner::CurrentDefaultHandle:從正在運行的任務發回 SequenceManager TaskQueues。
-
SequenceLocalStorageSlot :將外部狀態綁定到序列。
-
base::CurrentThread :與當前線程綁定的任務相關 API 子集的代理
-
嵌入器可以提供自己的靜態訪問器來在特定循環上發布任務(例如 content::BrowserThreads)。
SingleThreadTaskExecutor 和 TaskEnvironment
無需處理需要簡單任務發布環境(一個默認任務隊列)的 SequenceManager 和 TaskQueues 代碼,而是可以使用SingleThreadTaskExecutor。
單元測試可以使用高度可配置的TaskEnvironment 。
MessageLoop 和 MessageLoopCurrent
您可能會在代碼或文檔中看到對 MessageLoop 或 MessageLoopCurrent 的引用。這些類已不再存在,我們正在處理或刪除對它們的所有引用。base::MessageLoopCurrent
已被替換base::CurrentThread
,并且已刪除base::MessageLoop
arebase::SingleThreadTaskExecutor
和 的替換項base::Test::TaskEnvironment
。