Semaphore vs Mutex in Embedded C: Key Differences and Usage
mutex is a locking mechanism used to ensure exclusive access to a resource by one task at a time, while a semaphore can allow multiple tasks to access a resource up to a set limit. Mutexes are mainly for mutual exclusion, and semaphores can be used for signaling or counting available resources.Quick Comparison
This table summarizes the main differences between semaphore and mutex in embedded C.
| Feature | Mutex | Semaphore |
|---|---|---|
| Purpose | Mutual exclusion (single owner) | Resource counting or signaling |
| Ownership | Owned by the task that locks it | No ownership, any task can signal or wait |
| Value | Binary (locked/unlocked) | Integer count (0 to max count) |
| Use case | Protect shared data | Control access to limited resources or event signaling |
| Priority inversion handling | Usually supports priority inheritance | Usually no priority inheritance |
| Blocking behavior | Task blocks if mutex is locked | Task blocks if count is zero |
Key Differences
Mutex is designed to provide exclusive access to a resource by allowing only one task to own it at a time. It has ownership, meaning the task that locks the mutex must unlock it. This helps prevent race conditions when multiple tasks share data.
Semaphore is more general and can be used as a counter to control access to multiple instances of a resource or for signaling between tasks. It does not have ownership, so any task can signal (release) or wait (acquire) the semaphore.
In embedded C, mutexes often support priority inheritance to avoid priority inversion problems, where a low-priority task holds a mutex needed by a higher-priority task. Semaphores usually do not have this feature. Also, mutexes are binary (locked/unlocked), while semaphores can have a count greater than one.
Code Comparison
Here is an example of using a mutex in embedded C to protect a shared variable.
#include <stdio.h> #include <pthread.h> pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; int shared_data = 0; void* task(void* arg) { pthread_mutex_lock(&mutex); shared_data++; printf("Mutex locked: shared_data = %d\n", shared_data); pthread_mutex_unlock(&mutex); return NULL; } int main() { pthread_t t1, t2; pthread_create(&t1, NULL, task, NULL); pthread_create(&t2, NULL, task, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); return 0; }
Semaphore Equivalent
Here is an example of using a semaphore in embedded C to control access to a resource count.
#include <stdio.h> #include <pthread.h> #include <semaphore.h> sem_t semaphore; int shared_data = 0; void* task(void* arg) { sem_wait(&semaphore); shared_data++; printf("Semaphore acquired: shared_data = %d\n", shared_data); sem_post(&semaphore); return NULL; } int main() { sem_init(&semaphore, 0, 1); // Binary semaphore pthread_t t1, t2; pthread_create(&t1, NULL, task, NULL); pthread_create(&t2, NULL, task, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); sem_destroy(&semaphore); return 0; }
When to Use Which
Choose a mutex when you need strict mutual exclusion with ownership and priority inheritance to protect shared data from concurrent access. This is ideal for critical sections where only one task should run at a time.
Choose a semaphore when you need to manage access to a limited number of identical resources or for signaling events between tasks without ownership constraints. Semaphores are better for counting resources or simple task synchronization.