Chromium Chronicle #1:任务调度最佳实践

Chrome 团队很自豪地宣布推出 Chromium Chronicle,这是专门面向构建浏览器的 Chromium 开发者的月度系列博文。

《Chromium Chronicle》将主要侧重于传播技术知识和最佳实践,以便编写、构建和测试 Chrome。我们的计划是推出与 Chromium 开发者相关且实用的主题,例如代码运行状况、实用工具、单元测试、无障碍功能等!每篇文章都将由 Chrome 工程师撰写和修改。

我们对这个新系列感到非常兴奋,希望您也是如此!准备好深入探索了吗? 欢迎观看下方的第一集视频!

任务调度最佳实践

第 1 集:作者:Gabriel Charette 在巴基斯坦蒙特利尔市拍摄(2019 年 4 月)
上一集

需要进程内异步执行的 Chrome 代码通常会将任务发布到序列。序列是由 Chrome 管理的“虚拟线程”,因此最好不要创建自己的线程。对象如何知道应发布到哪个序列?

错误做法

旧范式是从创建器接收 SequencedTaskRunner:

Foo::Foo(scoped_refptr backend_task_runner)
    : backend_task_runner_(std::move(backend_task_runner)) {}
正确做法

首选范例是创建一个独立的 SequencedTaskRunner:

Foo::Foo()
    : backend_task_runner_(
          base::CreateSequencedTaskRunnerWithTraits({
              base::MayBlock(), base::TaskPriority::BEST_EFFORT})) {}

这样更易于读写,因为所有信息都是本地信息,并且不存在与不相关任务相互依赖的风险。

这种范式在测试方面也更好。测试可以实例化受控的任务环境来管理 Foo 的任务,而无需手动注入任务运行程序:

class FooTest : public testing::Test {
 public
  (...)
 protected:
  base::test::TaskEnvironment task_environment_;
  Foo foo_;
};

在夹具中优先使用 TaskEnvironment,这自然会确保它在整个 Foo 的生命周期内管理任务环境。TaskEnvironment 将捕获 Foo 在构建时的请求,以创建 SequencedTaskRunner,并将在每个 FooTest 下管理其任务。

如需测试异步执行的结果,请使用 RunLoop::Run()+QuitClosure() 范式

TEST_F(FooTest, TestAsyncWork) {
  RunLoop run_loop;
  foo_.BeginAsyncWork(run_loop.QuitClosure());
  run_loop.Run();
  EXPECT_TRUE(foo_.work_done());
}

此方法首选 RunUntilIdle(),如果异步工作负载涉及 TaskEnvironment 权限范围之外的任务(例如系统事件),该方法可能会不稳定,因此请谨慎使用 RunUntilIdle()

希望了解更多信息?请阅读我们关于线程处理和任务的文档,或参与到 TaskEnvironment 的迁移