Open Source im professionellen Einsatz
Linux-Magazin 10/2012
820

Kommunikationskanal

Zeile 36 von Listing 1 erzeugt den Promise, der einen »string« als Rückgabewert besitzt. Durch die Methode »get_future()« in Zeile 39 gibt dieser einen Future zurück, der durch einen Kanal mit ihm verbunden ist. Dieser Future erwartet einen »string« vom Promise. Der Arbeiter-Thread Bjarne erhält den Future als Parameter. Dabei verschiebt Zeile 42 mit »move(startWorkFuture)« den Future in einen neuen Thread.

Den Future nimmt der Thread in Zeile 21 mit »future<string>&&« an, einer so genannten Rvalue-Referenz. Dies ist notwendig, da ein Future sich nicht kopieren lässt. Schreibt der Boss in Zeile 46 durch den Aufruf »startWorkPromise.set_value("START YOUR WORK!")« den Wert in den Kanal, steht dieser bereit, um vom Arbeiter abgeholt zu werden. Da der Aufruf von »boss2Worker.get()« in Zeile 24 blockiert, muss der Arbeiter darauf warten, dass der Boss den Wert explizit setzt. Dieses Warten auf den Wert des Promise kann ein Future durch eine absolute oder relative Zeitangabe einschränken.

Das Meisterwerk

Stolz präsentiert der Boss seinen neu entworfenen Arbeitsablauf mit Listing 2 und führt ihn gleich aus (Abbildung 3). Der wesentliche Unterschied zu früheren Versionen ist, dass er mit seinen Arbeitern vier Datenkanäle verwendet: zwei, um Nachrichten von den Arbeitern zu empfangen, und zwei, um ihnen seine Anweisungen zu erteilen. So erhält der Arbeiter-Thread »herbWork()« in Zeile 59 die zwei Promises »herbPrepared« und »herbDone« , mit denen Herb signalisiert, dass er die Arbeit vorbereitet beziehungsweise vollendet hat. Da Promises nicht kopierbar sind, müssen sie wie im Prototyp verschoben werden.

Listing 2

Promise und Future (gekürzt)

01 [...]
02
03 int getRandomTime(int start, int end){
04
05 random_device seed;
06 mt19937 engine(seed());
07 uniform_int_distribution<int> dist(start,end);
08
09 return dist(engine);
10 };
11
12 class Worker{
13 public:
14 Worker(string n):name(n){};
15
16 void operator() (promise<void>&& preparedWork, shared_future<void> boss2WorkerStartWork,
17 promise<void>&& doneWork, shared_future<void>boss2WorkerGoHome ){
18
19 // prepare the work and notfiy the boss
20 int prepareTime= getRandomTime(500,2000);
21 sleep_for(milliseconds(prepareTime));
22 preparedWork.set_value();
23 cout << name << ": " << "Work prepared after " << prepareTime << " milliseconds." << endl;
24
25 // still waiting for the permission to start working
26 boss2WorkerStartWork.wait();
27
28 // do the work and notify the boss
29 int workTime= getRandomTime(200,400);
30 sleep_for(milliseconds(workTime));
31 doneWork.set_value();
32 cout << name << ": " << "Work done after " << workTime << " milliseconds." << endl;
33
34 // still waiting for the permission to go home
35 boss2WorkerGoHome.wait();
36
37 }
38 private:
39 string name;
40 };
41
42 int main(){
43
44 cout << endl;
45
46 // define the promise => Instruction from the boss
47 promise<void> startWorkPromise;
48 promise<void> goHomePromise;
49
50 // get the shared futures from the promise
51 shared_future<void> startWorkFuture= startWorkPromise.get_future();
52 shared_future<void> goHomeFuture= goHomePromise.get_future();
53
54 promise<void> herbPrepared;
55 future<void> waitForHerbPrepared= herbPrepared.get_future();
56 promise<void> herbDone;
57 future<void> waitForHerbDone= herbDone.get_future();
58 Worker herb(" Herb");
59 thread herbWork(herb,move(herbPrepared),startWorkFuture,move(herbDone),goHomeFuture);
60
61 // start thread scott, bjarne, andrei, andrew and david
62 [...]
63
64 cout << "BOSS: PREPARE YOUR WORK.\n " << endl;
65
66 // waiting for the worker
67 waitForHerbPrepared.wait(), waitForScottPrepared.wait(), waitForBjarnePrepared.wait(), waitForAndreiPrepared.wait(), waitForAndrewPrepared.wait(), waitForDavidPrepared.wait();
68
69 // notify the worker about the begin of the work
70 cout << "\nBOSS: START YOUR WORK. \n" << endl;
71 startWorkPromise.set_value();
72
73 // waiting for the worker
74 waitForHerbDone.wait(), waitForScottDone.wait(), waitForBjarneDone.wait(), waitForAndreiDone.wait(), waitForAndrewDone.wait(), waitForDavidDone.wait();
75
76 // notify the worker about the end of the work
77 cout << "\nBOSS: GO HOME. \n" << endl;
78 goHomePromise.set_value();
79
80 herbWork.join();
81 scottWork.join();
82 bjarneWork.join();
83 andreiWork.join();
84 andrewWork.join();
85 davidWork.join();
86
87 }

Abbildung 3: Der fertige Arbeitsablauf mit den Nachrichten von Boss und sechs Arbeitern.

Das Besondere an Herbs Benachrichtigung ist dieses Mal, dass er in den Zeilen 22 und 31 keinen Wert mitschickt, sondern nur den Boss benachrichtigt. Dies ist auch der Grund dafür, dass der Boss in Zeile 67 lediglich wartet, bis er Nachrichten von Herb und den restlichen Arbeitern erhält.

Um die Arbeiter zu informieren, dass sie mit der Arbeit beginnen beziehungsweise nach Hause gehen können, verwendet der Boss zwei »shared_future« , nämlich »startWorkFuture« und »goHomeFuture« . Während der Future »future« im Prototyp genau einen Sender mit einem Empfänger verbindet, erlaubt es der »shared_future« dem Boss, eine Benachrichtigung an alle Arbeiter zu verschicken (Zeilen 71 und 78). Im Gegensatz zum »future« ist der »shared_future« kopierbar, sodass man ihn nicht in den Arbeiter-Thread verschieben muss (Zeile 59). Listing 2 ist gekürzt wiedergegeben, die vollständige Version gibt es unter [5].

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

Stellenmarkt

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