Skip to main content

Thread Synchronization

Thread creation basics are covered in Threads (Basic). This page focuses on what comes next: safely sharing data and signaling between threads.



Semaphores — signaling between threads

static K_SEM_DEFINE(data_ready, 0, 1); /* initial=0, limit=1 */

/* Producer thread */
void sensor_thread(void *a, void *b, void *c)
{
while (1) {
fetch_sensor_data();
k_sem_give(&data_ready); /* signal consumer */
k_sleep(K_SECONDS(10));
}
}

/* Consumer thread */
void ble_thread(void *a, void *b, void *c)
{
while (1) {
k_sem_take(&data_ready, K_FOREVER); /* wait for signal */
update_gatt_characteristic();
}
}


Mutexes — protecting shared data

static K_MUTEX_DEFINE(data_mutex);
static float shared_temperature;

void writer_thread(void *a, void *b, void *c)
{
while (1) {
float temp = read_sensor();
k_mutex_lock(&data_mutex, K_FOREVER);
shared_temperature = temp;
k_mutex_unlock(&data_mutex);
k_sleep(K_SECONDS(5));
}
}

void reader_thread(void *a, void *b, void *c)
{
while (1) {
k_mutex_lock(&data_mutex, K_FOREVER);
float t = shared_temperature;
k_mutex_unlock(&data_mutex);
printk("Temp: %.2f\n", t);
k_sleep(K_SECONDS(1));
}
}


Message queues — passing data between threads

Safer than shared variables — no mutex needed:

struct sensor_msg {
float temperature;
float humidity;
};

K_MSGQ_DEFINE(sensor_msgq, sizeof(struct sensor_msg), 4, 4);

/* Producer */
struct sensor_msg msg = {.temperature = 24.3f, .humidity = 52.1f};
k_msgq_put(&sensor_msgq, &msg, K_NO_WAIT);

/* Consumer */
struct sensor_msg rx;
k_msgq_get(&sensor_msgq, &rx, K_FOREVER);


Work queues — deferring work from interrupts

GPIO interrupts run in interrupt context. Use a work queue to hand off to thread context:

static struct k_work button_work;

void button_work_fn(struct k_work *work)
{
/* Safe to do real work here */
printk("Button handled in thread context\n");
}

void button_isr(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
/* Fast — just submit the work */
k_work_submit(&button_work);
}

int main(void)
{
k_work_init(&button_work, button_work_fn);
/* ... set up GPIO interrupt ... */
}
warning

Stack overflow is the most common threading bug in Zephyr. If your board resets or behaves strangely, increase STACK_SIZE. Enable CONFIG_THREAD_ANALYZER=y to measure actual stack usage.



Thread priorities

Lower number = higher priority. Priority 0 is the highest. The idle thread runs at the lowest priority.

# Enable stack overflow detection
CONFIG_STACK_SENTINEL=y

# Print stack usage at runtime
CONFIG_THREAD_ANALYZER=y
CONFIG_THREAD_ANALYZER_AUTO=y