- Verwendete Werkzeuge
- Allgemeines
- Ein einfaches Beispiel
- Die beiden Klassen
std::packaged_taskundstd::functionim Vergleich - Ein zweites Beispiel
Klassen:
- Klasse
std::packaged_task - Klasse
std::future - Klasse
std::promise - Klasse
std::function
PackagedTask_01.cpp.
PackagedTask_02.cpp.
Das charakteristische Merkmal der Klasse std::packaged_task ist seine Fähigkeit,
ein aufrufbares Objekt (Callable) zu umschließen.
Ein auf diese Weise verpacktes Objekt wird nicht von alleine gestartet: Man muss den Aufruf explizit anstoßen – dies kann synchron im aktuellen Thread oder asynchron durch einen separaten Thread erfolgen.
Der Rückgabewert wird in einem std::future-Objekt abgelegt.
Für das Arbeiten mit std::packaged_task-Objekten sind typischerweise vier Schritte notwendig:
- Die Aufgabe in einem
std::packaged_task-Objekt verpacken bzw. aufbereiten. - Ein
std::future-Objekt erzeugen. - Die Berechnung (mit dem
std::packaged_task-Objekt) explizit anstoßen. - Das Ergebnis (mit
getamstd::future-Objekt) abholen.
Die soeben beschriebenen vier Schritte im Umgang mit der Klasse std::packaged_task
demonstriert folgendes Beispiel:
01: void test() {
02:
03: // create packaged_task object
04: std::packaged_task<int(void)> task {
05: [] () {
06: std::this_thread::sleep_for(std::chrono::seconds{ 1 });
07: return 123;
08: }
09: };
10:
11: // retrieve future object from task
12: std::future<int> future{ task.get_future() };
13:
14: // create a thread with this task
15: std::thread t{ std::move(task) };
16:
17: // retrieve result from future object
18: int result{ future.get() };
19: std::cout << "Result: " << result << std::endl;
20:
21: t.join();
22: }Ausgabe:
Result: 123
In diesem Beispiel wurde die Task asynchron in einem separaten Thread ausgeführt.
Es ginge aber auch synchron im aktuellen Thread, siehe dazu das nächste Beispiel:
01: void test() {
02:
03: // create packaged_task object
04: std::packaged_task<int(void)> task {
05: [] () {
06: std::this_thread::sleep_for(std::chrono::seconds{ 1 });
07: return 123;
08: }
09: };
10:
11: // retrieve future object from task
12: std::future<int> future{ task.get_future() };
13:
14: // execute task
15: task();
16:
17: // retrieve result from future object
18: int result{ future.get() };
19: std::cout << "Result: " << result << std::endl;
20: }Die beiden Klassen std::packaged_task und std::function besitzen Ähnlichkeiten.
Die Klasse std::packaged_task erzeugt „Callable Wrapper” Objekte,
ähnlich wie die Klasse std::function,
nur mit dem Unterschied, dass die Klasse std::packaged_task
einen direkten Zugriff auf ein korrespondierendes std::future-Objekt bietet.
Die Klasse std::packaged_task bietet folglich einen natürlicheren und einfacheren Arbeitsablauf
als das manuelle Einrichten und Durchschleusen eines std::promise-Objekts,
wie wir an folgendem Vergleichsbeispiel betrachten können:
01: static void test() {
02:
03: std::promise<int> promise;
04:
05: std::future<int> future{ promise.get_future() };
06:
07: std::function<void(std::promise<int>&&)> function {
08: [] (std::promise<int>&& promise) {
09: std::this_thread::sleep_for(std::chrono::seconds{ 1 });
10: promise.set_value(123);
11: }
12: };
13:
14: // create a thread with this function
15: std::thread t{ std::move(function), std::move(promise) };
16:
17: // retrieve result from future object
18: int result = future.get();
19: std::cout << "Result: " << result << std::endl;
20:
21: t.join();
22: }Ausgabe:
Result: 123
Ein einfaches Beispiel skizziert den Ablauf eines Szenarios mit vier std::packaged_task-Objekten.
Es ist die Summe der ersten 400 natürlichen Zahlen von 1 bis einschließlich 400 - ohne den Algorithmus von Gauss - zu berechnen.
Dabei kommen vier std::packaged_task-Objekte zum Einsatz.
Insbesondere betrachte man, dass sowohl std::packaged_task- als auch std::future-Objekte
in einem std::deque-Objekt ablegt werden können.
Hinweis:
Die Tasks können sowohl sequentiell als auch parallel ausgeführt werden.
Studieren Sie im Quellcode die entsprechenden Abschnitte,
wo eine Task entweder synchron oder asynchron abgearbeitet wird.