FreeRTOS Mutex einfach erklärt: Nie wieder Race Conditions pixeledi Tech Hub https://www.youtube.com/watch?v=cvcnyG7Vvg0 Transkript (automatisch erstellt) 0:00 Wenn wir FreeRTOS-Funktionen in unseren ESP32-Programmen verwenden, kann es sehr schnell passieren, dass wir in sogenannte Race Conditions reinlaufen. Das heißt, wir haben dann den Fall, dass wir vielleicht eine gemeinsame Variable oder Dateien bearbeiten und die aber von einer anderen Funktion währenddessen auch bearbeitet wird. 0:20 Warum das relativ schnell der Fall sein kann und man es oft gar nicht merkt und was wir dagegen tun können, das schauen wir uns in dem heutigen Video an. Die Überschrift hat es schon verraten, es geht nämlich um Mutex. 0:30 Ich habe wieder mein ursprüngliches Beispiel dabei, aber heute brauchen wir im Grunde nur den ESP32, zum Beispiel C3. Und der Fehler wirkt im Grunde eigentlich harmlos. 0:39 Wir haben zwei Tasks und eine gemeinsame Variable. Das heißt jetzt in dem Fall habe ich jetzt hier ein volatile uint32_t SharedCounter und das ist 0. 0:50 Jetzt kann man natürlich sagen, mit globalen Variablen soll man sowieso nicht arbeiten. Das dient jetzt einfach einmal nur der Visualisierung. 0:57 Es kann ja auch irgendwie ein Struct sein, was wir verwenden und das wird von zwei unterschiedlichsten Funktionen dann befüllt, wie zum Beispiel in der Praxis ist es ganz gern so, dass wir zum Beispiel von MQTT entweder Daten senden oder eben bekommen, das was hier irgendwie reingeliefert wird. Und dann haben wir zum Beispiel noch einen aktuellen Sensor, der ebenso dann Auswertungen macht und dann haben wir vielleicht noch einen dritten Task, der unseren Display updatet und der greift dann ebenso wieder drauf zu. 1:28 Das heißt, hier haben wir dann sehr wohl unterschiedlichste Informationen, die wir herholen wollen und mit FreeRTOS kann es dann eben sein, dass wir da zugleich zugreifen. Nochmal kurz zur Wiederholung, warum können wir gleichzeitig darauf zugreifen? 1:43 Wir haben ja hier dieses Beispiel letztes Mal gesagt mit dem Koch, der macht drei unterschiedliche Gerichte, aber die kann er natürlich nicht sofort parallel gleichzeitig kochen. Ist ja nicht möglich. 1:53 Und genau deshalb splittet er sich in Teiltasks auf und so können wir uns das vorstellen, auch auf dem ESP32-C3. Der C3 ist ja ein Single-Core, aber dementsprechend, wenn jetzt diese Task da aufgesplittet wird, haben wir den ersten Teil, den zweiten Teil, den dritten Teil und da haben wir da den ersten Teil, den zweiten und den dritten. 2:11 Und genau so haben wir dann eine Nebenläufigkeit, das scheint jetzt parallel abzulaufen für uns, aber im Hintergrund wird es einfach aufgesplittet. Und da sind wir jetzt im Grunde eigentlich schon im eigentlichen Problem drinnen. 2:22 Ich habe jetzt hier zur Veranschaulichung zwei unterschiedliche FreeRTOS-Funktionen erstellt. Es geht jetzt wirklich nur um eine Visualisierung. 2:31 Einmal einen Counter-Task 1, Counter-Task 2, der erste ohne Mutex, der zweite mit Mutex. Wenn wir da jetzt mal raufspringen, habe ich jetzt hier meine globale Variable und Achtung, dieses volatile ist nur dafür da, dass ich jetzt Compiler-Optimierungen verhindere, aber eben nicht, dass eine Race Condition passieren kann. 2:51 Ja, und schauen wir uns jetzt mal die erste Funktion an. Bevor wir das machen, lassen wir natürlich auf dem Bixlady-YouTube-Kanal unbedingt ein Abo da. 2:59 Und auch heute wieder ein ganz großes Dankeschön an alle meine bestehenden YouTube-Kanal-Mitglieder. Vielen Dank für euren Support. 3:07 Die erste Funktion ist jetzt hier Increment-Without-Mutex. Ich mache das jetzt bewusst so, dass ich hier einen Lesen, Erhöhen und Zurückschreiben, also einen Read, Modify, Write Ablauf mache und nicht gleich die Variable in einem Schritt inkrementiere. 3:24 Das ist, wie gesagt, einfach nur, damit man das besser darstellen kann. Aber in der Praxis ist das oft natürlich der Fall, wie eben oben dargestellt, MQTT-Display etc. 3:34 Gut, was mache ich jetzt? Ich lege mir da eine lokale Variable an und hole mir dann von oben den Wert, was in SharedCounter drinsteht. 3:40 In dem Fall wäre das jetzt zum Beispiel die 0. Dann mache ich eine Inkrementierung von plus 1, dann sollte jetzt hier 1 drinstehen und übergebe dann die Variable wieder retour. 3:51 Das heißt, da steht jetzt dann schlussendlich 1 drin. Jetzt wird man sich denken, okay, ist ja nicht wirklich viel dabei, macht ja alles Sinn. 3:59 Da herinnen mache ich jetzt folgendes. Ich sage dem FreeRTOS-Scheduler, hey, ich bin jetzt schon fertig, du kannst früher Schluss machen. 4:06 Denn im Normalfall haben wir da immer fix definierte Zeitabstände vom Scheduler. Und wenn wir schneller fertig sind, wie zum Beispiel, das braucht ja nicht wirklich viel Zeit, 4:16 kann ich sagen, ich bin schon fertig, dann braucht er jetzt nur diesen Teil anstatt diesen gesamten Teil. Das macht uns aber immer noch keine Race Condition. 4:23 Das heißt, das ist im Grunde nicht unser Problem. Das ist jetzt nur einfach zur Verdeutlichung, was man da noch alles machen kann. 4:29 Aber wo liegt jetzt unser Problem? Denn was wir nicht vergessen dürfen ist, dass jeder dieser Teilschritte unterbrochen werden kann. 4:39 Und genau das wird eben auch passieren, weil wir hier zwei Funktionen haben. Einmal hier und einmal die zweite. 4:44 Die haben wir uns noch gar nicht angesehen, aber die laufen ja quasi parallel. Und genau das aus diesem Grund passiert jetzt also. 4:50 Wenn jetzt hier eine Inkrementierung zum Beispiel stattfindet, kann da schon eine weitere Inkrementierung stattfinden. 4:57 Und das ist eben der große oder eines der Probleme, wenn wir eben nicht dann mit FreeRTOS und mit Mutex oder Semaphores arbeiten. Laufen wir jetzt einen Blick auf die zweite Funktion. 5:07 Wir holen uns jetzt hier eine Mutex und machen jetzt hier ein SharedCounter++, damit ich jetzt hier ganz schnell einfach eine Inkrementierung machen kann. 5:15 Und dann gebe ich wieder die Funktion frei. Das heißt, inzwischen jeder dieser einzelnen Schritte kann tatsächlich hier der SharedCounter++ gemacht werden. 5:26 Das heißt, ich hole mir hier die lokale Variable. In der Zwischenzeit, bevor jetzt das hier ausgeführt wird, wird dann dieser Schritt ausgeführt. 5:34 Und der ist ja exklusiv für den Mutex. Dann springt er wieder weiter und führt den nächsten Schritt aus. 5:40 Aber in der Zwischenzeit wird auch da wieder der Counter inkrementiert. Und genau aus diesem Grund habe ich dann, wenn wir ohne Mutex arbeiten, dann eine Differenz, eine erhebliche Differenz. 5:51 Und wenn wir mit Mutex arbeiten, sollten wir genau dieses Beispiel dann eben herausbekommen. Das heißt, wir sollten dann im Grunde, wenn wir dann unseren Ablauf machen, zuerst uns den Mutex holen, 6:03 dann eben unseren Ablauf machen mit Lesen, Verändern, Schreiben und dann geben wir den Mutex wieder frei, sodass hier während dieser Zeitperiode nichts passieren kann und keine Inhalte doppelt verändert werden können. 6:15 Wenn du jetzt also FreeRTOS auf dem ESP32 Schritt für Schritt lernen möchtest, dann wirf gerne mal einen Blick auf meinen FreeRTOS Online-Kurs, 6:23 beziehungsweise gibt es dazu eben auch noch ein extra E-Book dafür. Dort gehen wir das Thema Praxisnah mit ganz vielen Beispielen durch 6:29 und ebenso gibt es zu jedem Kapitel Übungen zu mitmachen und runterladen. Zusammenfassend und abschließend kann man jetzt also sagen, 6:36 Race Conditions entstehen immer dann, wenn ein Task zwischen Lesen und Schreiben unterbrochen wird. Der Mutex schützt deshalb den kompletten Read-Modify- und Write-Ablauf. 6:46 Das war schon der kurze Blick auf Mutex, da kann man natürlich Stunden drüber reden. Mich würde interessieren, hast du den schon einmal verwendet 6:52 oder bist du vielleicht sogar schon einmal in eine Race Condition reingelaufen? Checkt die Videobeschreibung, da ist dieses Beispiel noch einmal drin. 6:58 Ich wünsche euch viel Spaß beim Ausprobieren und Mitmachen. Wir sehen uns dann wieder bei einem der anderen Videos.