Die Boost C++ Bibliotheken

Futures und Promises

Futures und Promises stellen einen Mechanismus dar, Daten von einem Thread an einen anderen zu übergeben. Während dies auch zum Beispiel über globale Variablen bewerkstelligt werden kann, kommt die Datenübergabe mit Futures und Promises ohne globale Variablen aus. Darüberhinaus müssen Sie sich nicht selbst um eine Synchronisation kümmern.

Ein Future ist eine Variable, die einen Wert erhalten wird, der von einem anderen Thread berechnet wird. Wenn Sie auf ein Future zugreifen, um die Variable auszulesen, müssen Sie womöglich warten, bis der entsprechende Thread den Wert errechnet hat. Boost.Thread stellt den Typ boost::future zur Verfügung, um ein Future zu definieren. Die Klasse bietet eine Methode get() an, um den Wert der Variablen zu erhalten. Diese Methode ist blockierend, da sie unter Umständen auf einen Thread warten muss.

Um ein Future auf einen Wert zu setzen, muss auf ein mit dem Future verbundenes Promise zugegriffen werden. Während boost::future eine Methode get() besitzt, existiert keine Methode, um einen Wert zu setzen.

Für Promises stellt Boost.Thread die Klasse boost::promise bereit. Diese Klasse besitzt eine Methode set_value(). Der Trick ist, dass Futures und Promises als Paar auftreten. So kann über die Methode get_future() ein Future von einem Promise erhalten werden. Future und Promise können in unterschiedlichen Threads verwendet werden. Wird das Promise in einem Thread gesetzt, kann der entsprechende Wert über den Future im anderen Thread gelesen werden.

Beispiel 44.14. boost::future und boost::promise in Aktion
#define BOOST_THREAD_PROVIDES_FUTURE
#include <boost/thread.hpp>
#include <boost/thread/future.hpp>
#include <functional>
#include <iostream>

void accumulate(boost::promise<int> &p)
{
  int sum = 0;
  for (int i = 0; i < 5; ++i)
    sum += i;
  p.set_value(sum);
}

int main()
{
  boost::promise<int> p;
  boost::future<int> f = p.get_future();
  boost::thread t{accumulate, std::ref(p)};
  std::cout << f.get() << '\n';
}

Im Beispiel 44.14 kommen ein Future und ein Promise zum Einsatz. Das Future f wird über get_future() vom Promise p erhalten. Daraufhin wird eine Referenz auf den Promise an den Thread t übergeben, der die Funktion accumulate() ausführt. In accumulate() wird die Summe der Zahlen 1 bis 5 errechnet und dann im Promise gespeichert. In main() wird lediglich get() für den Future aufgerufen, um den entsprechenden Wert in die Standardausgabe zu schreiben.

Das Future f und das Promise p sind verknüpft. Wenn für das Future get() aufgerufen wird, wird der Wert zurückgegeben, der mit set_value() im Promise gesetzt wurde. Da das Beispiel aus zwei Threads besteht, ist es möglich, dass get() in main() aufgerufen wird, bevor in acccumulate() set_value() aufgerufen wurde. In diesem Fall blockiert get() und kehrt erst zurück, wenn mit set_value() ein Wert im Promise gespeichert wurde.

Wenn Sie Beispiel 44.14 ausführen, wird 10 ausgegeben.

Das Beispiel erfordert, dass die Funktion accumulate() für den Einsatz in einem Thread angepasst wurde. So muss die Funktion einen Parameter vom Typ boost::promise erwarten und in diesem das Ergebnis speichern. Im folgenden Beispiel lernen Sie die Klasse boost::packaged_task kennen, mit der Sie jede beliebige Funktion verwenden können, die das Ergebnis mit return zurückgibt.

Beispiel 44.15. boost::packaged_task in Aktion
#define BOOST_THREAD_PROVIDES_FUTURE
#include <boost/thread.hpp>
#include <boost/thread/future.hpp>
#include <utility>
#include <iostream>

int accumulate()
{
  int sum = 0;
  for (int i = 0; i < 5; ++i)
    sum += i;
  return sum;
}

int main()
{
  boost::packaged_task<int> task{accumulate};
  boost::future<int> f = task.get_future();
  boost::thread t{std::move(task)};
  std::cout << f.get() << '\n';
}

Beispiel 44.15 ist vergleichbar mit dem vorherigen. Diesmal wird jedoch kein Promise vom Typ boost::promise verwendet. Stattdessen kommt boost::packaged_task zum Einsatz. Diese Klasse bietet wie boost::promise eine Methode get_future() an, um ein Future zu erhalten.

Dem Konstruktor von boost::packaged_task muss eine Funktion übergeben werden, die in einem Thread ausgeführt werden soll. boost::packaged_task selbst startet keinen Thread. Ein Objekt vom Typ boost::packaged_task muss an den Konstruktor von boost::thread übergeben werden, damit der Thread startet und die entsprechende Funktion im Thread ausgeführt wird.

Der Vorteil von boost::packaged_task ist, dass der Rückgabewert der entsprechenden Funktion im Future gesetzt wird. Es ist nicht notwendig, eine Funktion dahingehend zu ändern, dass sie ein Ergebnis in einem Promise speichert. boost::packaged_task ist in gewisser Weise ein Adapter, der den Rückgabewert einer Funktion in einem Future ablegt.

Während im obigen Beispiel boost::promise weggelassen werden konnte, wird im folgenden zusätzlich auf boost::packaged_task und boost::thread verzichtet.

Beispiel 44.16. boost::async() in Aktion
#define BOOST_THREAD_PROVIDES_FUTURE
#include <boost/thread.hpp>
#include <boost/thread/future.hpp>
#include <iostream>

int accumulate()
{
  int sum = 0;
  for (int i = 0; i < 5; ++i)
    sum += i;
  return sum;
}

int main()
{
  boost::future<int> f = boost::async(accumulate);
  std::cout << f.get() << '\n';
}

Im Beispiel 44.16 wird accumulate() an die Funktion boost::async() übergeben. Diese Funktion vereint boost::packaged_task und boost::thread. Sie startet accumulate() in einem Thread und gibt ein Future vom Typ boost::future zurück.

Sie können boost::async() eine Launch-Policy übergeben. Dieser zusätzliche Parameter bestimmt, ob boost::async() die Funktion in einem neuen oder im aktuellen Thread ausführt. Wenn Sie boost::launch::async als ersten Parameter an boost::async() übergeben, wird ein neuer Thread gestartet. Übergeben Sie boost::launch::deferred, wird die Funktion im aktuellen Thread ausgeführt. boost::launch::async ist der Standardwert, der gilt, wenn Sie keine Launch-Policy angeben.

In Boost 1.57.0 fehlt die Implementation für boost::launch::deferred. Übergeben Sie boost::launch::deferred an boost::launch::async, wird Ihr Programm sofort beendet.