C++26: Die Sender-Fabriken, Adapter und Consumer von std::execution

Das Framework std::execution für Asynchronität und Parallelität bietet drei Arten von Sendern : Fabriken, Adapter und Consumer.

vorlesen Druckansicht 4 Kommentare lesen
HolzwĂĽrfel mit dem Schriftzug C++

(Bild: SerbioVas/Shutterstock)

Lesezeit: 4 Min.
Von
  • Rainer Grimm
Inhaltsverzeichnis

Die Komposition von Sendern sowie Erläuterungen zum asynchronen Ausführen von Inclusive Scan mit std::execution habe ich bereits in meinen vorangegangenen Beiträgen behandelt. Nun widme ich mich den drei Arten von Sendern, die std::execution bietet: Fabriken, Adapter und Consumer.

Modernes C++ – Rainer Grimm
Rainer Grimm

Rainer Grimm ist seit vielen Jahren als Softwarearchitekt, Team- und Schulungsleiter tätig. Er schreibt gerne Artikel zu den Programmiersprachen C++, Python und Haskell, spricht aber auch gerne und häufig auf Fachkonferenzen. Auf seinem Blog Modernes C++ beschäftigt er sich intensiv mit seiner Leidenschaft C++.

Die folgenden Informationen zu den Sendern stammen überwiegend aus dem Proposal P2300R10. Ich werde hier versuchen, die Inhalte etwas prägnanter darzustellen.

Eine Sender Factory ist ein Algorithmus, der keine Sender als Parameter verwendet und einen Sender zurĂĽckgibt.

execution::schedule

 execution::sender auto schedule(
    execution::scheduler auto scheduler
);

Gibt einen Sender zurĂĽck, der mit dem bereitgestellten Scheduler startet.

execution::just

execution::sender auto just(
    auto ...&& values
);

Gibt einen Sender zurĂĽck, der die bereitgestellten Werte sendet.

execution::just_error

execution::sender auto just_error(
    auto && error
);

Gibt einen Sender zurĂĽck, der mit einem spezifischen Fehler abschlieĂźt.

execution::just_stopped

execution::sender auto just_stopped();

Gibt einen Sender zurück, der sofort durch Aufruf von set_stopped des Empfängers abgeschlossen wird.

execution::read_env

execution::sender auto read_env(auto tag);

Gibt einen Sender zurück, der die Umgebung eines Empfängers und den aktuellen Wert ausliest, der mit dem tag-Umgebungswert verknüpft ist. Anschließend sendet er den gelesenen Wert über den Wertkanal an den Empfänger zurück. read_env(get_scheduler) ist beispielsweise ein Sender, der den Empfänger nach dem aktuell vorgeschlagenen Scheduler fragt und ihn an das set_value-Abschluss-Signal des Empfängers weitergibt.

Dies kann bei der Planung von verschachtelten, abhängigen Arbeiten nützlich sein. Der folgende Sender zieht den aktuellen Scheduler in den Wertkanal und plant dann weitere Arbeiten darauf.

Ein Sender-Adapter ist ein Algorithmus, der einen oder mehrere Sender als Parameter verwendet und einen Sender zurĂĽckgibt.

Sender-Adapter sind lazy. Sender-Consumer wie this_thread::sync_wait starten Sender.

execution::continues_on

execution::sender auto continues_on(
    execution::sender auto input,
    execution::scheduler auto scheduler
);

Gibt einen Sender zurĂĽck, der den Ăśbergang vom AusfĂĽhrungsagenten des Eingabesenders zum AusfĂĽhrungsagenten des Ziel-Schedulers beschreibt.

execution::then

Gibt einen Sender zurĂĽck, der das vom Eingabesender beschriebene Task-Diagramm beschreibt. Dabei fĂĽgt er einen Knoten zum Aufrufen der bereitgestellten Funktion hinzu, mit den vom Eingabesender als Argumente gesendeten Werten.

execution::upon_*

upon_error und upon_stopped sind then ähnlich, aber während then vom Eingabesender gesendeten Werten arbeitet, arbeitet upon_error mit Fehlern, und upon_stopped wird aufgerufen, wenn das "stopped"-Signal gesendet wird.

Ein anschauliches Beispiel fĂĽr then, upon_error und upon_stopped ist die Prototypbibliothek stdexec.

Das folgende Beispiel zeigt einen HTTP- Request-Handler.

/*
 * Copyright (c) 2022 Lucian Radu Teodorescu
 *
 * Licensed under the Apache License Version 2.0 with LLVM Exceptions
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *   https://llvm.org/LICENSE.txt
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
 // Handler for the "classify" request type
ex::sender auto handle_classify_request(const http_request& req) {
  return
    // start with the input buffer
    ex::just(req)
    // extract the image from the input request
    | ex::then(extract_image)
    // analyze the content of the image and classify it
    // we are doing the processing on the same thread
    | ex::then(do_classify)
    // handle errors
    | ex::upon_error(on_classification_error)
    // handle cancellation
    | ex::upon_stopped(on_classification_cancelled)
    // transform this into a response
    | ex::then(to_response)
    // done
    ;
}

Die Funktion extrahiert Bilder und gibt ein ex::sender-Objekt zurück, das einen asynchronen Vorgang darstellt, der aus anderen Vorgängen bestehen kann.

Die Funktion nimmt einen konstanten Verweis auf einen http_request req. Die Verarbeitungspipeline beginnt mit der ex::just(req)-Funktion, die einen Sender erstellt, der mit dem Eingabepuffer der HTTP-Anfrage beginnt.

Die Pipeline verwendet dann die ex::then-Funktion, um eine Reihe von Vorgängen zu verketten. Der erste Vorgang, extract_image, extrahiert das Bild aus der Eingabeanforderung. Der nächste Vorgang klassifiziert den Inhalt des extrahierten Bildes. Diese Verarbeitung erfolgt im selben Thread.

Die Fehlerbehandlung wird mithilfe der Funktion ex::upon_error in die Pipeline integriert, die die Funktion on_classification_error zur Behandlung von Fehlern angibt, die während des Klassifizierungsprozesses auftreten.

Ebenso wird die Funktion ex::upon_stopped verwendet, um die Beendigung des Vorgangs zu behandeln, wobei die Funktion on_classification_cancelled angegeben wird.

SchlieĂźlich wird die Funktion to_response verwendet, um das Klassifizierungsergebnis in eine HTTP-Antwort umzuwandeln. Diese Umwandlung erfolgt ebenfalls mit der ex::then-Funktion.

Dieser Code veranschaulicht eine zusammensetzbare und asynchrone Verarbeitungspipeline fĂĽr die Bearbeitung von HTTP-Anfragen, die Klassifizierung von Bildern sowie Fehler und AbbrĂĽche.

execution::starts_one

execution::sender auto starts_on(
    execution::scheduler auto sched,
    execution::sender auto snd
);

Dieser Sender-Adapter ändert die Ausführungsressource, auf der der Sender ausgeführt wird. Er passt den Sender nicht an.

std::execution hat noch weitere Sender-Adapter zu bieten, die ich meinen nächsten Beiträgen vorstellen werde: execution::let_*, execution::into_variant, execution::stopped_as_optional, execution::stopped_as_error, execution::bulk, execution::split und execution::when_all. (map)