Chromium Chronicle #1: sprawdzone metody dotyczące planowania zadań

Zespół Chrome z dumą prezentuje Chromium Chronicle, comiesięczną serię stworzoną specjalnie dla programistów Chromium, którzy tworzą przeglądarkę.

Chronicle w Chromium skoncentruje się przede wszystkim na rozpowszechnianiu wiedzy technicznej i sprawdzonych metod związanych z pisaniem, kompilowaniem i testowaniem Chrome. Planujemy umieścić w niej tematy istotne i przydatne dla programistów Chromium, takie jak stan kodu, przydatne narzędzia, testowanie jednostkowe czy ułatwienia dostępu. Każdy artykuł będzie tworzony i edytowany przez inżynierów Chrome.

Cieszymy się z nowej serii i mamy nadzieję, że Ty też. Zaczynamy? Obejrzyj pierwszy odcinek poniżej.

Sprawdzone metody dotyczące planowania zadań

Odcinek 1: Gabriel Charette w Montrealu, PQ (kwiecień 2019 r.)
Poprzednie odcinki

Kod Chrome, który wymaga asynchronicznego wykonywania w trakcie, zazwyczaj publikuje zadania w sekwencjach. Sekwencje to zarządzane przez Chrome „wirtualne wątki” preferowane do tworzenia własnych wątków. Skąd obiekt wie, w jakiej kolejności publikować?

Nie

Stary model polegał na otrzymaniu od twórcy SequencedTaskRunner:

Foo::Foo(scoped_refptr backend_task_runner)
    : backend_task_runner_(std::move(backend_task_runner)) {}
Tak

Preferowanym modelem jest utworzenie niezależnego procesu SequencedTaskRunner:

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

Łatwiej się wtedy czytać i pisać, ponieważ wszystkie informacje są lokalne i nie ma ryzyka współzależności z innymi, nieistotnymi zadaniami.

Ten model sprawdza się również w przypadku testowania. Zamiast ręcznie wstrzykiwać aplikacje uruchamiające zadania, testy mogą uruchomić kontrolowane środowisko zadań do zarządzania zadaniami Foo:

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

Użycie środowiska TaskEnvironment na pierwszym miejscu w sposób naturalny zapewnia zarządzanie środowiskiem zadań przez cały okres działania Foo. TaskEnvironment przechwyci żądanie Foo podczas tworzenia w celu utworzenia obiektu SequencedTaskRunner i będzie zarządzać jego zadaniami w każdym teście FooTest.

Aby przetestować wynik wykonania asynchronicznego, użyj paradygmatu RunLoop::Run()+QuitClosure():

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

Jest to zalecane w przypadku metody RunUntilIdle(), która może być niestabilna, jeśli asynchroniczne zadanie zadań obejmuje zadanie spoza zakresu TaskEnvironment, np. zdarzenie systemowe, dlatego należy używać RunUntilIdle() z rozwagą.

Chcesz dowiedzieć się więcej? Przeczytaj naszą dokumentację na temat podziału wątków i zadań lub weź udział w migracji do TaskEnvironment.