Unsere Datenschutzrichtlinie wird in Kürze aktualisiert. Bitte sehen Sie sich die Vorschau an.

C++: Multithreading

Tasks versus Bedingungsvariablen

Testen Sie unsere 2016 Kurse

10 Tage kostenlos!

Jetzt testen Alle Abonnements anzeigen
Tasks sind fast immer die bessere Alternative zu Bedingungsvariablen.
07:28

Transkript

In dierser Lektion möchte ich Ihnen die Synchronisation con Threads mit Hilfe von Bedingungsvariablen und von Tasks genau vorstellen. Um was geht es? Es geht darum, dass Sie ein Thread haben, der schickt eine Benachrichtigung. Aud diese Benachrichtigungen wartet ein anderer Thread und dieser Thread beginnt dann mit seiner Arbeit, wenn er die Benachrichtigung bekommt. Typischerweise erlegt der Sender eine wichtige Aufgabe davor und wenn er diese Arbeit vorllendet hat, dann schikt es seine Benachrichtigung, seine Notification auf die eben der Empfänger wartet, um mit seiner Arbeit vortzusetzen. Dieses Szenario können Sie sowohl mit Bedingungsvariablen, wie auch mit Tasks, sprich Promise und Future implementieren. Daher möchte ich die beiden Techniken gegenüberstellen, mit dem Hintergedanken verwenden Sie Promise and Future, da lauern viel viel weniger gefahren. Fangen wir mal oben an in der Tabelle. Mit Bedingungsvariablen können Sie mehrmalig syncroniseren, das heißt, eine Bedingungsvariable kann merhmals verwendet werden, um zwischen Threads zu syncronisieren, also [unverstandlich] ist da mehrmals möglich. Das ist mit Tasks nicht möglich. Da können Sie nur einmal eine Benachrichtigung schicken und wenn der Promise die Benachrichtigung geschickt hat und de Future Sie abgeholt hat , dann müssen Sie ein neues Promise-Future-Paar verwenden. "Kritischer Bereich". Bei Bedingungsvariablen haben Sie einen kritischer Bereich. Einen kritischen Bereich, wenn Sie Mutexen versus Locks explizit schützen müssen. Das haben Sie bei Promise und Future oder Tasks nicht. Was heißt das? Promise und Future interargieren über einen sicheren Datenkanal. Da gibt es keinen gemeinsamen, da gibt es keinen shared Zusand. Wenn im Sender eine Ausnahme geworfen wird, dann haben Sie keine Chance die im Empfänger zu fangen und damit umzugehen. Ihre Threads beenden sich automatisch und damit auch Ihr Programm. So nicht bei Tasks. Bei Tasks können Sie auch eine Ausnahme schicken, das heißt, der Promise schickt eine Ausnahme und der Future muss halt mit dieser Ausnahme dann umgehen. "Spurios wakeup", das sind die zwei Phänomene, die jetzt kommen, "Spurios wakeup" und "Lost wakeup", die ich am schwierigsten finde bei Bedingungsvariablen, weil es mit diesen extrem schwierig ist richtig umzugehen. Spurios wakeup ist das Phänomen, das der Empfänger der Nachricht aufwacht, obwohl keine Benachrichtigung vorliegt. Durch irgendein Hintergrund [unverständlich]. Das ist ein bekanntes Phänomen bei Threads. und da gibt es noch das Phänomen des Lost wakeup. Das Lost wakeup ist das Phänomen, dass der Sender die Benachrichtigung schickt, aber der Empfänger noch gar nicht im Zustand ist zu warten, der hört also gar noch nicht auf den Sender. Das ist wie ein Wecker der schellt, aber Sie sind im falschen raum. Das Ergebnis ist, Sie haben ein Deadlock, das heißt, der Empfänger der Nacheicht wartet und wartet und wartet. So, das möchte ich Ihnen jetzt natürlich noch am Beispiel zeigen. Hier habe ich einen typischen Arbeitsverlauf vorbereitet. Sie sehen das Ergenbis, das ist immer das Gleiche, egal ob ich conditionVariable oder promiseFuture, um Threads zu synchronisieren. Was ist mein Arbeitsablauf? Der Worker wartet bis er mit seiner Arbeit beginnen kann, dann sagt der Sender: "Data is ready". Nun kann der Worker sein Arbeitspaket erledigen und zum Schluss kann der sagen, "ich bin fertig". Genau die gleichen Schritte passieren hier. Und jetzt zeige ich Ihnen den Code, für die conditionVariable und für promiseFuture. Fangen wir mal hier oben an. Hier sehen Sie schonmal, ich habe einen Mutex. Den Mutex benötige ich bei der conditionVariable, um den Zugriff auf die gesharete Variable, das ist in diesem Fall "dataReady" und den Zugriff auf den "wait" zu schützen. Okay, das brauche ich hier nicht. Ich habe hier keinen kritischen Bereich, ich habe zwei Endpunkte eines Datakanals, die per se unabhängig voneinander sind. das ist hier der Waiter, das ist hier der Waiter, das heißt, diese Funktion wird im wartenden Thread, und diese Funktion wird auch im wartenden Thread ausgeführt. Was muss ich hier machen? Ich ignoriere mal die Ausgaben auf Standard out. Zuerst benötigt der Mutex, verpacke ich in den Lock, und jetzt kann ich auf meine Benachrichtigung warten, indem ich den Lock hier verwende und indem ich das zusätzliche Prädikat hier verwende. Sie wissen, das zusätzliche Prädikat benötigen Sie, um sich gegen Lost und Spurious wakeup zu schützen, weil Sie, in "wait" hier aufgeweckt werden, wissen Sie nicht, ob das ein zufälliges Aufwecken war oder ob das [unverständlich] war. Die [unverständlich] habe ich nur ein fut.wait( ) Aufruf. Die Sache ist also deutlich einfacher zu programmieren. Jetzt schaue ich hier mit Ihnen noch die Senderseite an, sprich der Sender-Thread. Hier benötige ich auch wieder den Mutex. Den Mutex benötige ich aus dem Grund, weil ich "dataReady=true" setzen möchte und "dataReady=true" ist einfach keine automare Variable. Ich verwende ihn also für "dataReady=true" und dann kann ich sagen, "notify_one". Hier benachrichtigt der Sender den Empfänger, dass er loslegen kann. Im Gegensatz dazu hier, gibt es einfach einen Aufruf "set_Value", wieder kein Mutex, keine zusätzliche Variable "dataReady", um anzudeuten, dass der Sender fertig ist, ist also auch wieder deutlich einfacher. Ja, und jetzt noch zum Hauptprogramm. Da unterscheiden sich die Programme deutlich. Hier habe ich ein thread t1, der "waitingforWork" ausführt und hier habe ich ein thread t2, der "setDataReady" ausführt, Okay. Was habe ich hier? Hier habe ich ein "Promise sendReady". Diesen Promise verwende ich, um hier ein Future zu erzeugen. Nun habe ich den Datenkanal, zwischen dem Promise und dem Future. Und was ich nun tue ist sowohl die Funktion "readyForWork" im eigenen Thread ausführen, als auch die Funktion "setDataReady" im eigenen Thread ausführen. Die Feinheit ist hier, dass ich sowohl den Future, als auch den Promise in diesen Thread reinverschieben muss. Sie wissen Promise und Future sind nicht kopierbar, man kann Sie nur verschieben. Sie unterstützen nur die Move-Semantik. Ja, und hier geht es analog weiter. Sie sehen, wenn Sie die Anzahl der Zeilen vergleichen, ist es tatsächlich auf der Promise- und Futureseite nicht deutlich weniger, nur sechs Zeilen weniger. Aber das entscheidende ist, Sie haben keinen kritischen Bereich. Sie müssen keinen Mutex verwenden, den Sie auch in den Lock packen und damit können die klassischen Probleme wie Deadlocks oder ein DataRace, ein kritischer Wettlauf, weil Sie auf einer nichtautomaren Variable gleichzeitig ein Les- und ein Schreibzugriff haben, nicht auftauchen.

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!