C++: Multithreading

Sender und Empfänger

LinkedIn Learning kostenlos und unverbindlich testen!

Jetzt testen Alle Abonnements anzeigen
Bedingungsvariablen stellen ein einfaches Werkzeug dar, Aktionen von Threads zu synchronisieren. Während ein Thread eine Benachrichtigung verschickt, wenn er fertig ist, wartet der andere Thread auf dessen Benachrichtigung, um mit seiner Arbeit fortzufahren.
08:00

Transkript

In dieser Lektion möchte ich Ihnen Bedingungsvariablen vorstellen. Bedingungsvariablen ermöglichen es, Threads über Benachrichtigungen zu synchronisieren. Das heißt, ein Thread ist typischerweise Sender, der andere ist Empfänger. Oder auch Producer – Consumer. Der Sender schickt eine Benachrichtigung auf die Warte der Empfänger und sobald der Empfänger die Benachrichtigung erhält, kann er mit seiner Arbeit weitermachen. Producer ist gleiches Szenario, und in diesem Fall produziert der Producer noch ein Ergebnis, auf das der Consumer wartet, um eben weiterzumachen seine Arbeit. std::condition_var ist der Name der Bedingungsvariable. Sie benötigen der Header . Und eine Condition-Variable kann beide Rollen annehmen. Sowohl die Rolle des Senders, wie auch die Rolle des Empfängers. Bevor ich weiter mache, noch eine wichtige Anmerkung. Zur Synchronisation von Threads sind Tasks meist die einfachere Lösung. Wenn Sie also solche Arbeitsabläufe wie Sender-Empfänger, Producer-Consumer zwischen Threads etablieren wollen, ist die einfache Lösung in meisten Fällen gleich Tasks zu verwenden. Auf der einen Seite haben wir den Sender. Der Sender schickt eine Benachrichtigung. Die Benachrichtigungen können an einen oder mehrere Threads gehen. Bzw. eine Bedingungsvariable oder mehreren Bedingungsvariablen. Wir haben also Empfänger, er kann in verschiedenen Arten auf die Benachrichtigungen warten. Er kann warten, bis er die Benachrichtigung bekommt, wait. Er kann warten für eine relative Zeit, also eine Zeitdauer, oder kann bis zu einem Zeitpunkt warten. Und die drei Punkte hier, auf die will ich dann noch genauer eingehen. Die wait-Methode kann und sollte auch ein zusätzliches Prädikat verwenden. Prädikat ist eine Funktion, die es in Wert True oder False gibt. Was hat die Funktion für eine Aufgabe in wait? Wenn der schlafende oder wartende Thread auf seine Benachrichtigung wartet und die Benachrichtigung erhält, kontrolliert er nicht nur, ob die Benachrichtigung tatsächlich angekommen ist, sondern kontrolliert auch, ob das Prädikat True gibt. Wieso ist es notwendig? Weil es eben zwei sehr anspruchsvolle Phänomene gibt, bei Bedingungsvariablen. So genannte spurious wakeup und lost wakeup. Was ist ein spurious wakeup? Da verwende ich eine Analogie. Es kann durchaus passieren, wenn ein Thread auf einen anderen Thread wartet, und er dann die Benachrichtigung erhält, dass der wartende Thread vermeintlich die Benachrichtigung bekommen hat, tatsächlich war aber das keine Benachrichtigung. Das ist wie, wenn Sie im Bett legen, und die Katze kratzt an der Tür, und Sie glauben, es war der Wecker. Das ist just Hintergrund-Geräusch. Das ist das Phänomen des spurious wakeup. Da gibt es noch das Phänomen des lost wakeup. Das lost wakeup ist das Phänomen, dass Ihr Wecker läutet und Sie sind noch gar nicht in dem Zustand, auf den Wecker zu hören. Das heißt zum Beispiel, Ihr Wecker läutet, bevor Sie ins Bett gehen. Das ist lost wakeup. Sie schlafen dann für ewig. Anders ausgedrückt, es werden nicht schöne Arten, den Lock zu produzieren. Gut, und weil es diese Phänomene gibt, schützt man sich mit dem Prädikat dagegen. Hier stelle ich Ihnen mal den Arbeitsablauf dar. Der Thread1 ist der Sender. Der macht seine Arbeit und irgendwann ist er fertig, er benachrichtigt den Empfänger. do the work, dann benachrichtigt er den Empfänger durch notify(). Und er verwendet diesen zusätzlichen boolischen Wert, den er eben auf true setzt, damit der Empfänger genau weiß, er ist wirklich zu Recht aufgeweckt worden. Und da, dass hier ein Bool-Wert ist, keine atomare Variable, muss ich ihn mit dem lock_guard schützen. So, wie geht es weiter? Auf der anderen Seite haben wir den Empfänger. Er wartet mit dem Lock auf seine Benachrichtigung, erhält das Lock, prüft und schläft. Und das macht er so lang, bis eben die Benachrichtigung da ist. Wenn die Benachrichtigung da ist, macht es seine Arbeit, gibt das Lock wieder frei. Hier hat es unique_lock, die beiden synchronisieren sich auf die gleichen mutex. mut, mut. Hier ist der Wartezustand. condVar.notify() synchronisiert mit condVar.wait(). Also, condVar.wait() wartet, zieht das lck einmalig kurz, prüft, ob er mittlerweile benachrichtigt wurde, indem er auch noch zusätzlich drauf achtet, ob es kein Hintergrund-Geräusch war, oder spurious wakeup oder lost wakeup. Und wenn da Condition war, dass er die Benachrichtigung bekommen hat, fährt er mit seiner Arbeit fort. Einen feinen Unterschied gibt es noch. Da ich dieses ready=true nur einmal locken muss, reicht der lock_guard aus. Da hier mein lck dauernd gelockt und angelockt wird, möchte ich mächtigeren Lock, den unique_lock. Ja, der letzte Schritt fehlt noch. Irgendwann erhält der wartende Thread die Benachrichtigung vom Sender, und dann setzt er mit seiner Arbeit fort. Nun kommt man schon zum Beispiel. Hier ist mein Beispiel. Ich habe ein Thread t1, der wartet für was, waitingForWork. Und ich habe ein Thread t2, der ruft die Funktion setDataReady auf. Jetzt schauen wir uns mal die zwei Funktionen an. waitingForWork(), die ist für das wartende Thread. Der gibt am Anfang „Worker:Waiting for work.“ aus. Dann verwendet er das unique_lock, um den mutex zu erhalten. Und hier ist eine Schleife drin. Hier wartet er immer im kurzen Zeitpunkt, lockt wieder und prüft gleichzeitig, ob dataReady gesetzt ist, bis er die Benachrichtigung bekommt. Wenn er die Benachrichtigung bekommen hat, führt dann doTheWork() aus. Und zum Schluss gibt aus „Work done.“. Das ist Empfänger. Was macht der Sender? setDataReady(). Das ist der Producer oder der Sender. Der verwendet das gleiche mutex_ hier, setzt, während er den mutex_ besitzt, spricht den mutex_ gelockt hat. dataReady auf true, dataReady entspricht keine atomare Variable. Also muss ich sie schützen. Dann gibt er „Sender: Data is ready.“ aus. Und zum Schluss schickt er eine Benachrichtigung. Ich habe hier ein ganz kleines Problem mit dem Source. Gut, das will ich jetzt fixen. Das Problem ist, dass Sie den Lock so kurz, wie möglich halten müssen. Also soll ich den Lock so kurz halten, wie es absolut notwendig ist. Es reicht vollkommen aus, wenn dieses Lock nur… Jetzt sieh. Das heißt, das einzige, was ich schützen muss, ist dataReady. Dass std::cout nicht geschützt ist, stört mich in diesem Fall nicht. Ich habe tatsächlich kein Problem. Und dieser Aufruf hier ist so und so atomar. Also, jetzt sieht es ein bisschen besser, das Performance des Programms. Ja, und dann gibt es noch die Funktion doTheWork(). Hier habe ich untergeschlagen, aber da sage ich nur „Processing shared data.“. Und wenn ich jetzt das Programm ausführe, sehen Sie genau, in welcher Reihenfolge die Threads ausgeführt werden. Erst mal „Worker:Waiting for work.“ Der wartet mal. Dann kommt der Sender. Der sagt dann. Was sagt der den? „Sender: Data is ready.“ Wenn „Data ready“ ist, wird doTheWork() ausgeführt. „Processing shared data.“ Und zum Schluss sagt er noch „Work done.“. Das sagt in diesem Fall der Empfänger oder der Consumer.

C++: Multithreading

Lernen Sie die High-Level Threading-Schnittstelle in C++ kennenb und nutzen, die Sie in Form von Threads, Tasks, Locks und Bedingungsvariablen zur Anwendung bringen.

2 Std. 40 min (39 Videos)
Derzeit sind keine Feedbacks vorhanden...
Software:
Exklusiv für Abo-Kunden
Erscheinungsdatum:16.08.2016

Dieser Online-Kurs ist als Download und als Streaming-Video verfügbar. Die gute Nachricht: Sie müssen sich nicht entscheiden - sobald Sie das Training erwerben, erhalten Sie Zugang zu beiden Optionen!

Der Download ermöglicht Ihnen die Offline-Nutzung des Trainings und bietet die Vorteile einer benutzerfreundlichen Abspielumgebung. Wenn Sie an verschiedenen Computern arbeiten, oder nicht den ganzen Kurs auf einmal herunterladen möchten, loggen Sie sich auf dieser Seite ein, um alle Videos des Trainings als Streaming-Video anzusehen.

Wir hoffen, dass Sie viel Freude und Erfolg mit diesem Video-Training haben werden. Falls Sie irgendwelche Fragen haben, zögern Sie nicht uns zu kontaktieren!