Chromium Chronicle #1: лучшие практики планирования задач

Команда Chrome с гордостью представляет Chromium Chronicle, ежемесячную серию, специально предназначенную для разработчиков Chromium, разработчиков, создающих браузер.

Chromium Chronicle в первую очередь сосредоточится на распространении технических знаний и лучших практик написания, сборки и тестирования Chrome. Наш план — освещать темы, которые актуальны и полезны для разработчиков Chromium, такие как работоспособность кода, полезные инструменты, модульное тестирование, специальные возможности и многое другое! Каждая статья будет написана и отредактирована инженерами Chrome.

Мы в восторге от этой новой серии и надеемся, что вы тоже! Готовы погрузиться? Посмотрите наш первый выпуск ниже!

Лучшие практики планирования задач

Эпизод 1: Габриэль Шаретт, Монреаль, PQ (апрель 2019 г.)
Предыдущие серии

Код 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 !