1. Singleton Pattern
The Singleton Pattern ensures that a class has only one instance and provides a global access point.
Core structure diagram of the Singleton Pattern:

The structure typically includes:
- A private static instance (pointer to itself)
- A private constructor (to prevent external instantiation)
- A public static method (to get the unique instance)
In embedded systems, this pattern is particularly suitable for:
- Global State: System configuration, error logs
- Hardware Peripheral Management: SPI/I2C controllers
- Resource Pool: Memory pool, connection pool
- Core Manager: Power management, security controllers
Key points in embedded design:
- Thread Safety: Critical sections must be protected by disabling interrupts or using mutexes (especially in RTOS).
- No Dynamic Memory: Prefer static allocation of instances to avoid heap memory fragmentation.
2. Embedded Application Case
Maintaining system state in a remote monitoring system.
- Non-Singleton Pattern: Each module (sensor module, control module, communication module) maintains its own system state, leading to:
- The temperature sensor believes the system is normal
- But the control module detects a fault
- Inconsistent states lead to dangerous operations
- Singleton Pattern: Establish a central console (the unique instance), all modules access the system state through it, ensuring global decision consistency.

1. Code Implementation:
In C language:

#include <stdio.h>
#include <pthread.h>
// Global state structure
typedef struct {
int system_mode;
int error_code;
unsigned long operation_count;
} SystemState;
// Singleton state manager
typedef struct {
SystemState state;
pthread_mutex_t lock;
} StateManager;
// Global singleton instance
static StateManager* instance = NULL;
// Get singleton instance (thread-safe)
StateManager* get_state_manager() {
if (instance == NULL) {
// Create mutex
static pthread_mutex_t init_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&init_lock);
if (instance == NULL) {
static StateManager manager;
manager.state.system_mode = 0;
manager.state.error_code = 0;
manager.state.operation_count = 0;
pthread_mutex_init(&manager.lock, NULL);
instance = &manager;
}
pthread_mutex_unlock(&init_lock);
}
return instance;
}
// Set system mode
void set_system_mode(int mode) {
StateManager* manager = get_state_manager();
pthread_mutex_lock(&manager->lock);
manager->state.system_mode = mode;
pthread_mutex_unlock(&manager->lock);
}
// Get system mode
int get_system_mode() {
StateManager* manager = get_state_manager();
pthread_mutex_lock(&manager->lock);
int mode = manager->state.system_mode;
pthread_mutex_unlock(&manager->lock);
return mode;
}
// Report error
void report_error(int error_code) {
StateManager* manager = get_state_manager();
pthread_mutex_lock(&manager->lock);
manager->state.error_code = error_code;
pthread_mutex_unlock(&manager->lock);
}
// Increment operation count
void increment_operation_count() {
StateManager* manager = get_state_manager();
pthread_mutex_lock(&manager->lock);
manager->state.operation_count++;
pthread_mutex_unlock(&manager->lock);
}
// Print system state
void print_system_state() {
StateManager* manager = get_state_manager();
pthread_mutex_lock(&manager->lock);
printf("\n=== System State ===\n");
printf("Current Mode: %s\n",
manager->state.system_mode == 0 ? "Normal Mode" :
manager->state.system_mode == 1 ? "Maintenance Mode" : "Emergency Mode");
printf("Error Code: 0x%04X\n", manager->state.error_code);
printf("Operation Count: %lu\n", manager->state.operation_count);
pthread_mutex_unlock(&manager->lock);
}
// Simulate sensor module
void* sensor_module(void* arg) {
printf("Sensor starting...\n");
for (int i = 0; i < 3; i++) {
increment_operation_count();
printf("Sensor detecting...\n");
sleep(1);
}
return NULL;
}
// Simulate control module
void* control_module(void* arg) {
printf("Controller starting...\n");
sleep(1);
// Simulate error detection
report_error(0xE1A2);
set_system_mode(2); // Enter emergency mode
printf("Controller detected a serious error!\n");
return NULL;
}
int main() {
printf("=== Control System Starting ===\n");
// Initialize state manager
get_state_manager();
print_system_state();
// Create simulation threads
pthread_t sensor_thread, control_thread;
pthread_create(&sensor_thread, NULL, sensor_module, NULL);
pthread_create(&control_thread, NULL, control_module, NULL);
// Wait for threads to finish
pthread_join(sensor_thread, NULL);
pthread_join(control_thread, NULL);
// Print final state
print_system_state();
printf("\n=== System Shutdown ===\n");
return 0;
}

In C language, there is no concept of classes, so we simulate the Singleton Pattern using static global variables and functions.
-
Private Static Instance: A static global pointer
<span>static StateManager* instance = NULL;</span>points to the unique instance. The instance itself is created using a static local variable<span>static StateManager manager;</span>within the getter function. This way, the instance is initialized only once during the program’s execution and returned via a pointer. -
Private Constructor: In C, this is achieved through static initialization or initialization within the getter function. In the
<span>get_state_manager()</span>function, the instance is created using a static local variable and initialized only once. -
Public Static Method: This is the
<span>get_state_manager()</span>function, which returns a pointer to the singleton instance. We also provide other functions to operate on the state, such as<span>set_system_mode()</span>,<span>get_system_mode()</span>, etc., which internally call<span>get_state_manager()</span>to get the singleton instance.
In C++:
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
class StateManager {
private:
struct State {
int system_mode = 0;
int error_code = 0;
unsigned long operation_count = 0;
} state;
std::mutex mtx; // Non-static member lock, protects instance state
StateManager() = default; // Private constructor
public:
// Delete copy constructor and assignment operator
StateManager(const StateManager&) = delete;
void operator=(const StateManager&) = delete;
// Use local static variable to implement thread-safe singleton
static StateManager& getInstance() {
static StateManager instance;
return instance;
}
void setSystemMode(int mode) {
std::lock_guard<std::mutex> lock(mtx);
state.system_mode = mode;
state.operation_count++;
}
int getSystemMode() {
std::lock_guard<std::mutex> lock(mtx);
return state.system_mode;
}
void printState() {
std::lock_guard<std::mutex> lock(mtx);
std::cout << "Mode: " << state.system_mode
<< " | Errors: 0x" << std::hex << state.error_code
<< " | Operations: " << std::dec << state.operation_count << "\n";
}
};
// Test function
void threadTask(int mode) {
StateManager& manager = StateManager::getInstance();
manager.setSystemMode(mode);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
manager.printState();
}
int main() {
std::vector<std::thread> threads;
// Create multiple threads to test thread safety
for (int i = 1; i <= 5; ++i) {
threads.emplace_back(threadTask, i);
}
// Wait for all threads to finish
for (auto& t : threads) {
t.join();
}
// Main thread access
StateManager::getInstance().printState();
return 0;
}
2. Advantages and Disadvantages
Advantages:
(1) Resource Efficiency:
- Memory Savings: Only one instance, reducing RAM occupied by duplicate objects (critical for resource-constrained MCUs).
- Performance Improvement: Avoid frequent creation/destruction of objects (e.g., communication protocol stacks), reducing CPU overhead.
(2) Behavioral Consistency:
- Prevents hardware state conflicts (e.g., multiple tasks configuring UART baud rates simultaneously).
- Ensures global data (e.g., calibration parameters) is uniquely reliable.
Disadvantages:
(1) Poor Scalability: Lack of abstraction layer makes it difficult to extend functionality through inheritance (violating the Open/Closed Principle).
(2) Lifecycle Issues: Static instances occupy memory for a long time, wasting resources if not utilized.
3. Summary of Applicability in Embedded Scenarios

| Scenario | Reason |
|---|---|
| Hardware Peripheral Management (SPI/UART) | Prevent multi-task conflicts |
| Global Configuration Manager | Ensure parameter consistency |
| High-frequency Object Creation/Destruction | Reduce CPU overhead |
| Shared Utility Classes Across Modules | Simplify access but increase coupling |

END
Author: Mixed Content
Source: Embedded MiscellaneousCopyright belongs to the original author. If there is any infringement, please contact for deletion..▍Recommended ReadingSharing several BootLoaders suitable for MCUWhy some domestic chips do not disclose data sheets?Incredible! These 17 C pointer tricks have been collected by countless programmers overnight→ Follow for more updates ←