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

Chrome 团队很荣幸地向大家推出 Chromium Chronicle, 该系列文章面向的是 Chromium 开发者, 。

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

我们对这个新的系列充满期待,希望您也是如此!准备好深入探索了吗? 欢迎观看下面的第一集视频!

任务安排最佳做法

第 1 集:作者:Gabriel Charette in Montréal,PQ(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