Das Schlüsselwort volatile teilt dem Compiler mit, dass eine Variable auf unerwartete Weise geändert werden kann (z. B. durch Hardware, einen anderen Thread oder einen Interrupt-Handler) und verbietet es, ihren Wert zwischenzuspeichern oder den Zugriff darauf zu optimieren.
Verwendung:
volatile garantiert keine Atomizität oder Synchronisation!).volatile int flag = 0; void handler() { flag = 1; // Interrupt-Handler } void loop() { while (!flag) { // auf Ereignis warten } // ... }
Ohne volatile könnte der Compiler die Schleife in eine endlose Schleife umwandeln (würde flag nicht aus dem Speicher lesen), mit volatile wird die Variable jedes Mal aus dem Speicher gelesen.
Reicht es aus, volatile zu verwenden, um den korrekten Informationsaustausch zwischen Threads zu gewährleisten?
Ein häufiger Fehler ist zu denken, dass volatile die Speichersynchronisation zwischen Threads gewährleistet und atomare Operationen durchführt.
Richtige Antwort:
volatile schützt nicht vor Datenrennen in einer Multithread-Umgebung und gewährleistet keine Speicherrückhalteeffekte: Es sagt nur dem Compiler, keine Optimierungen für den Zugriff vorzunehmen. Für garantierte Korrektheit müssen Synchronisationsprimitive (z. B. mutex, atomare Operationen usw.) verwendet werden.
// Dies ist unsicher! volatile int ready = 0; void thread1() { data = 123; // Daten schreiben ready = 1; // Signal für einen anderen Thread } void thread2() { while (!ready); // auf Ereignis warten printf("data = %d\n", data); // möglicherweise sind die Daten noch nicht aktualisiert! }
Geschichte
Ein Fehler bei der Arbeit mit Hardware-Registern: Werte wurden ohne volatile geschrieben/gelesen, und der Compiler optimierte den Zugriff, was zu einer vollständigen Ignorierung neuer Registerwerte führte – einige Befehle funktionierten nicht.