Und um hier nochmal zu erklären warum das so kompliziert sein muss:

Threads haben einen eigenen Kontext in dem sie arbeiten und in dem alle Daten als vertrauenswürdig erscheinen!
Ein Thread KANN THEORETISCH hingehen und Variablen in einem anderen Thread verändern wie es ihm beliebt ... ob der andere Thread damit klarkommt oder einfach ins leere greift, weil du die Flex auf einen anderen Platz gelegt hat ist fragwürdig und daher von den meisten Thread Bibliotheken verboten und resultiert zum Beispiel in Java in einer Exception wegen falschem Thread Zugriff!

Man kann sogenannte ATOMICS einsetzen, dabei werden Passagen des Programms innerhalb eines Threads für "nicht unterbrechbar" erklärt und niemand kann während dieser Sequenz irgendwelche Variablen verändern oder die Ausführung dieser Sequenz behindern/beenden

Man kann auch überall yield() oder sleep() einsetzen, wann immer man der Überzeugung ist, dass alle Variablen und auch der Zustand des Threads es erlauben die Kontrolle über das Programm an einen anderen Thread abzugeben, welcher dann nach belieben Variablen in dem wartenden Prozess zu verändern. Bei Microcontrollern REICHT DIESE METHODE VOLLKOMMEN AUS!!!

Bei Mehrkernsystemen kann es aber dazu kommen, dass das yield() ignoriert wird und der Thread weiterarbeitet, weil es mehr als einen echten Kern gibt! Also kann es sein, dass Thread A estwas in Thread B verändern will, der aber stur weiterarbeitet und du somit keinen Zugriff erhälst (laute Flex, eingeschränkes Gehör und Sicht) ... also musst du joinen/anklopfen ... und dann macht Thread B an einer der definierten Stellen (yield(), sleep(),...) eine ECHTE Pause und wartet dass du dein Join beendest und er weiter machen kann!

ACHTUNG WICHTIG falls du den Merhkernpart übersprungen hast weil er dich nicht trifft, ist es dennoch gute Praxis immer ein join/mutex/atomic zu nutzen um konsistente daten zwischen den Threads zu gewährleisten!