"Single-task stable, multi-task chaotic"—this is the global variable curse for embedded developers. While global variables can quickly achieve module data sharing, in RTOS multi-task, interrupt-driven, and main program concurrent scenarios, they are prone to data corruption: voltage jumps, count anomalies, and parameter disorder. This article thoroughly explains the core solutions for thread safety and clarifies the selection logic of mutexes and atomic operations.

First, understand: Why do global variables "crash"?

Variable assignment at the CPU level is often a "non-atomic operation." For example, assigning a value to a 32-bit global variable will be split into two 16-bit writes on an ARM architecture CPU. If interrupted and the variable is modified in the middle, the final result will be a "mixed value of the old and new," directly causing data corruption.

For example, in a water meter project, a global variable records water consumption. The main task reports and interrupts accumulate the data. After going live, frequent "water consumption regressions" occurred. The root cause is "data races" caused by concurrent operations—this is the most typical pitfall of global variables.

General Protection: Mutex Locks, Adding a "Safety Door" to Global Variables

The logic of a mutex lock is similar to a bathroom door lock: it locks when one person uses it, and others wait in line. It adds "access permissions" to global variables, ensuring that only one task operates on them at a time, fundamentally avoiding concurrent conflicts.

Its advantage is its "universal adaptability," protecting both numeric variables and complex data such as structures and arrays. For example, locking a production parameter structure in an industrial controller ensures complete read and write operations, preventing the problem of "modification after partial reading."

Three pitfalls to avoid when using mutex locks: ① Must be "released immediately after use," as failing to release can lead to deadlock; ② A dedicated safety interface is required during interrupts; ③ The logic within the lock should be concise, avoiding time-consuming operations such as delays and printing that slow down the system.

High-Efficiency Solution: Atomic Operations, Lightweight "Lockless Protection"

Adding mutex locks to all global variables will degrade efficiency due to frequent task switching. For simple operations like counting and flag switching, atomic operations are superior—they complete the operation with a single CPU instruction, uninterrupted by other tasks, much like a sprint where a false start is invalid; once the instruction is started, it must be executed completely.

For example, when a task's counter increments, atomic operations eliminate the need for an allocation-release process, completing the operation with a single instruction. This is safe, efficient, and without performance loss. It relies on hardware support, does not depend on an RTOS, and can be used in bare-metal development.

Atomic operations have clear boundaries: they only apply to basic data types within 32 bits (integers, booleans), and operations are limited to reading, writing, and incrementing/decrementing. Complex data such as structures require multiple instructions, which atomic operations cannot cover. Furthermore, 8-bit MCU atomic operations only support 8-bit data; always consult the chip datasheet before use.

Ultimate Choice Guide: When and Which?

When struggling to choose between the two, the core considerations are data type and operational complexity.

Choose a mutex: ① Complex operations (batch changes, reading/writing structures/arrays); ② Requires exclusive resource access. Prioritize scenarios with high data integrity requirements, such as industrial control and medical equipment.

Choose atomic operations: ① Simple data (integers, flags); ② Simple operations (insertions, assignments); ③ High real-time requirements (high-frequency interrupt counting). First choice for low-latency scenarios such as IoT sensors and smart homes.

Final 3 Tips to Avoid Pitfalls

1. Prioritize using local variables + parameters instead of global variables to control risks at the source;

2. Avoid nested mutexes to prevent deadlocks caused by mutual locks between tasks A and B;

3. Don't overuse atomic operations; forcing complex logic with them can easily lead to hidden errors.

Thread safety of global variables is essentially about "order management of concurrent access." Simple operations rely on atomic operations for efficiency, while complex scenarios rely on mutexes for safety. Remember this core principle, and you'll no longer need to worry about crashes caused by global variables. This collection guide will help you identify and address any concurrent issues!