Open Source im professionellen Einsatz
Linux-Magazin 10/2012

Modernes C++ in der Praxis – Folge 6

Alle im Einklang

Promise und Future erweisen sich als nützliche Neuerungen im C++11-Standard. Das Gespann macht die bisher aufwändige Synchronisation mehrerer Threads einfach und übersichtlich.

552

Dieser Beitrag der C++11-Reihe knüpft an den im Linux-Magazin 08/12 an [1]. Er vollendet das Programmierbeispiel vom Boss und seinen sechs Mitarbeitern. Um die Arbeitskraft seiner Arbeiter in geregelte Bahnen zu lenken, setzt der Chef diesmal Promise (Versprechen) und Future (Zukunft) ein. Wie diese neuen Features funktionieren und warum sie die mühselige Synchronisation der Arbeiter-Threads durch atomare Variablen, Mutexe, Locks und Bedingungsvariablen überflüssig machen, zeigt dieser Artikel.

Compilerversionen

Nur der aktuelle GCC 4.7 kann die Codebeispiele aus diesem Artikel übersetzen. Denn die älteren GCC-Implementierungen setzen voraus, dass die Argumente des Thread kopiert werden. Weder Promise noch Future sind jedoch kopierbar.

Arbeiten auf Zuruf

Die Aufgabe, vor der der Boss steht, ist schnell skizziert (Abbildung 1) – Leser der vorigen Folgen kennen sie bereits: Die Arbeiter Herb, Scott, Bjarne, Andrei, Andrew und David benachrichtigen den Boss, sobald sie die Vorbereitungen für ihre Arbeit abgeschlossen haben. Sobald der Boss alle Benachrichtigungen erhalten hat, gibt er ihnen das Kommando, das Tagwerk zu beginnen. Jeder der sechs Arbeiter teilt später dem Boss mit, wenn er mit seiner Arbeit fertig ist.

Abbildung 1: Der Arbeitsablauf für die Arbeiter Herb, Scott, Bjarne, Andrei, Andrew und David.

Hat der Boss diese Nachricht von allen erhalten, ist das Arbeitspensum erledigt und er schickt alle in den Feierabend. Damit gibt es vier Synchronisationspunkte für ein Programm, das diesen Arbeitsablauf mittels mehrerer Threads abbildet: Zwei muss der Boss einhalten, die anderen beiden die Arbeiter.

Ein erster Versuch

Dieser Arbeitsablauf ist nicht so trivial, wie er klingt. Verständlich, dass der Boss zuerst einen Prototyp entwirft und diesen mit seinem Vorarbeiter Bjarne testet, zumal er mit Future und Promise vollkommenes Neuland betritt (Listing 1). Auf der informativen Wikiseite C++ Reference [2] lässt sich das ganze Leistungsspektrum des Paares Promise [3] und Future [4] nachlesen. Das Ergebnis des Prototyps sieht vielversprechend aus. Das Paar »promise« in Zeile 36 und »future« in Zeile 39 baut einen Datenkanal auf, in dem Promise als Datensender und Future als Datenempfänger dient.

Listing 1

Prototyp

01 #include <future>
02 #include <iostream>
03 #include <thread>
04 #include <utility>
05
06 using std::cout;
07 using std::endl;
08
09 using std::string;
10 using std::move;
11
12 using std::promise;
13 using std::future;
14
15 using std::thread;
16
17 class Worker{
18 public:
19   Worker(string n):name(n){};
20
21     void operator() (future<string>&& boss2Worker){
22
23       // still waiting for the permission to start working
24       string message= boss2Worker.get();
25       cout << "Worker from Boss: " << message << endl;
26     }
27 private:
28   string name;
29 };
30
31 int main(){
32
33   cout << endl;
34
35   // define the promise => Instruction from the boss
36   promise<string> startWorkPromise;
37
38   // get the futures from the promise
39   future<string> startWorkFuture= startWorkPromise.get_future();
40
41   Worker bjarne("Bjarne");
42   thread bjarneWork(bjarne,move(startWorkFuture));
43
44   // notify the worker about the begin of the work
45   cout << "Boss: Notifying the worker. \n" << endl;
46   startWorkPromise.set_value("START YOUR WORK!");
47
48   bjarneWork.join();
49
50   cout << endl;
51
52 }

Dabei ist es weder notwendig, dass die beiden Kommunikationsendpunkte sich in verschiedenen Threads befinden, noch müssen tatsächlich Daten geschickt werden. Beispielsweise kann der Promise auch nur eine Benachrichtigung oder eine Ausnahme an den Future schicken. Exemplarisch sind Datensender und -empfänger in Abbildung 2 dargestellt.

Abbildung 2: Promise und Future als Sender und Empfänger von Nachrichten.

Diesen Artikel als PDF kaufen

Express-Kauf als PDF

Umfang: 3 Heftseiten

Preis € 0,99
(inkl. 19% MwSt.)

Linux-Magazin kaufen

Einzelne Ausgabe
 
Abonnements
 
TABLET & SMARTPHONE APPS
Bald erhältlich
Get it on Google Play

Deutschland

Ähnliche Artikel

  • C++11

    Die C++11-Reihe beschäftigt sich weiter mit dem Synchronisieren von Threads. Diesmal setzt der Chef-Thread Bedingungsvariablen ein, um die Tätigkeit seiner Mitarbeiter-Threads zu koordinieren.

  • C++11

    Die neue Zeitbibliothek von C++11 erweist sich als elementarer Bestandteil der Threading-Schnittstelle: Sowohl Threads, Locks und Bedingungsvariablen als auch Futures haben ein Verständnis von Zeit. Dank ihrer Unterstützung kann ein Entwickler unterschiedliche Wartestrategien verfolgen.

  • Java-Threads

    Seit der ersten Version von Java sind Threads ein fester Bestandteil der Sprache. Das macht vieles einfacher als in anderen Programmiersprachen. Neuere Versionen der Java-Bibliothek bieten darüber hinaus viele nützliche Klassen für Locking und Synchronisierung.

  • C++

    Beim deklarativen Programmieren drückt der C++-Programmierer unter anderem mit Hilfe von Schlüsselwörtern aus, was er erreichen möchte. Der Compiler kümmert sich dann um den weiteren Weg.

  • Sauber eingefädelt

    Der Standard für Threads unter Linux ist heute die Native Posix Threads Library (NPTL). Die Bibliothek überzeugt durch große Kompatibilität zum Standard und hohe Performance. Dieser Artikel untersucht die neue Threading-Engine und zeigt, wie Benutzeranwendungen davon profitieren.

comments powered by Disqus

Ausgabe 10/2017

Digitale Ausgabe: Preis € 6,40
(inkl. 19% MwSt.)

Artikelserien und interessante Workshops aus dem Magazin können Sie hier als Bundle erwerben.