Chromium Chronicle #1: Best Practices für die Aufgabenplanung

Das Chrome-Team ist stolz, Chromium Chronicle vorzustellen, eine monatliche Reihe speziell für Chromium-Entwickler, die den Browser entwickeln.

Chromium Chronicle konzentriert sich in erster Linie darauf, technisches Wissen und Best Practices zum Schreiben, Erstellen und Testen von Chrome zu verbreiten. Unser Plan ist es, Themen zu präsentieren, die für Chromium-Entwickler relevant und nützlich sind, z. B. Codeintegrität, hilfreiche Tools, Unittests, Barrierefreiheit und vieles mehr. Jeder Artikel wird von Chrome-Entwicklern verfasst und bearbeitet.

Wir freuen uns auf diese neue Serie und hoffen, du bist es auch! Bist du bereit? Hier ist unsere erste Folge.

Best Practices für die Aufgabenplanung

Folge 1:von Gabriel Charette aus Montreal, PQ (April 2019)
Vorherige Folgen

Chrome-Code, der eine asynchrone Ausführung während der Verarbeitung erfordert, sendet in der Regel Aufgaben an Sequenzen. Sequenzen sind von Chrome verwaltete "virtuelle Threads" und werden bevorzugt zum Erstellen eines eigenen Threads verwendet. Woher weiß ein Objekt, in welcher Sequenz es gepostet werden soll?

Don'ts

Das alte Modell besteht darin, einen SequencedTaskRunner vom Creator zu erhalten:

Foo::Foo(scoped_refptr backend_task_runner)
    : backend_task_runner_(std::move(backend_task_runner)) {}
Das sollten Sie tun:

Das bevorzugte Modell besteht darin, einen unabhängigen SequencedTaskRunner zu erstellen:

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

Dies ist einfacher zu lesen und zu schreiben, da alle Informationen lokal sind und kein Risiko von Wechselabhängigkeiten mit irrelevanten Aufgaben besteht.

Dieses Modell ist auch besser, wenn es um Tests geht. Anstatt Aufgaben-Runner manuell einzufügen, können Tests eine kontrollierte Aufgabenumgebung instanziieren, um die Aufgaben von Foo zu verwalten:

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

Wenn TaskEnvironment an erster Stelle im Display hat, wird die Aufgabenumgebung während der gesamten Lebensdauer von Foo verwaltet. Die TaskEnvironment erfasst die Anfrage von Foo bei der Konstruktion, um einen SequencedTaskRunner zu erstellen und verwaltet seine Aufgaben unter jedem FooTest.

Verwenden Sie das RunLoop::Run()+QuitClosure()-Paradigma, um das Ergebnis einer asynchronen Ausführung zu testen:

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

Dies wird gegenüber RunUntilIdle() bevorzugt, da es unzuverlässig sein kann, wenn die asynchrone Arbeitslast eine Aufgabe umfasst, die nicht in den Zuständigkeitsbereich der TaskEnvironment fällt, z. B. ein Systemereignis. Verwenden Sie RunUntilIdle() mit Vorsicht.

Möchten Sie mehr erfahren? Lesen Sie unsere Dokumentation zu Threading und Aufgaben oder machen Sie sich an der Migration zu TaskEnvironment.