Two Ways to Create Threads in Zephyr RTOS
Thread
In Zephyr OS, each independent function or task can be placed in a different thread for execution. A lightweight scheduler is used internally to decide which thread can be executed by the CPU first, based on the priority of different threads and the state of the thread itself (ready, running, pending, etc.). Threads can have the following properties:
- Thread Priority: The smaller the value, the higher the priority (in the case of Preemptive scheduling).
- Stack Size: Each thread must configure its own stack space. Zephyr saves or restores the thread context when switching threads.
- Thread Lifecycle: Can be pre-configured at compile time, or dynamically configured and created at run time.
Two Common Methods to Create Threads in Zephyr
Create Thread at Compile Time
Zephyr provides a macro K_THREAD_DEFINE() to declare and create threads at compile time. As long as the program starts and initializes RTOS, these threads will be automatically created and managed by the scheduler.
K_THREAD_DEFINE()
| |
- name: Identifier name of this thread, which will also generate a variable of type k_tid_t.
- stack_size: Stack size of this thread (in bytes).
- entry_fn: Entry point function of this thread (thread function), which function to run when execution starts.
- p1, p2, p3: Up to three parameters can be passed to the entry point function (all void* type).
- prio: Priority (smaller value means higher priority).
- options: Thread options, usually 0 implies no special settings.
- delay: Delay time before the thread starts (ticks or milliseconds). If 0, it means no delay and ready immediately.
Create Thread at Run Time
Another way is to create threads during program execution, using the API function k_thread_create(). This method is more flexible in some cases, for example, dynamically starting or terminating threads based on states or conditions, to avoid wasting resources by statically creating too many.
k_thread_create()
| |
- new_thread: Points to a struct k_thread object declared by the user, used to store the Thread Control Block (TCB) of this thread.
- stack: Points to the stack space corresponding to this thread (needs to be statically or dynamically allocated with
K_THREAD_STACK_DEFINE()first). - stack_size: Size of this stack.
- entry: Entry point function executed by the thread.
- p1, p2, p3: Up to three parameters to pass to the thread function.
- prio: Priority.
- options: Thread options, commonly 0.
- delay: Whether to delay before the thread starts.
Example
The following two code snippets demonstrate creating two threads using K_THREAD_DEFINE() (at compile time) or k_thread_create() (at run time) respectively, executing LED fading (fade_led) and LED blinking (toggle_led) functions.
github repo
The following is the execution result on Nucleo-F303K8, the left LED executes toggle, the right one executes fading.

Example of Dynamic Thread Creation at Compile Time
K_THREAD_DEFINE(fade_tid, 512, fade_led, NULL, NULL, NULL, 5, 0, 0);- Create a thread named fade_tid, stack size 512 bytes, executing function fade_led, priority 5.
- p1, p2, p3 are all NULL, indicating no parameters need to be passed.
K_THREAD_DEFINE(toggle_tid, 512, toggle_led, NULL, NULL, NULL, 5, 0, 0);- Create a thread named toggle_tid similarly, stack size and priority parameters consistent with above, executing function toggle_led.
These threads are already “statically allocated” during compilation, so when the system starts and executes to main(), they will be automatically started and continuously executed by the scheduler.
| |
Example of Dynamic Thread Creation at Run Time
If you want to create threads during program execution (e.g., triggered by an event), you can use the following method:
1. Use K_THREAD_STACK_DEFINE(my_stack_area, STACK_SIZE); to define stack.
2. Define a struct k_thread my_thread_data; as Thread Control Block (TCB).
3. Call k_thread_create(), passing the above stack, control block, entry point function and other parameters to complete creation.
The following is an example showing how to dynamically create threads in the main() function (this example only shows parts related to threads, omitting repetitive code like peripheral initialization):
| |
Comparison
| Creation Method | Features | Typical Use Cases |
|---|---|---|
| Compile Time Creation | K_THREAD_DEFINE() | When required threads are known from the start, and system resources are sufficient |
| Run Time Dynamic Creation | k_thread_create(), allocate resources during program execution | When unsure how many threads are needed, or need flexible creation/release |
- If it is certain that the number of threads is fixed throughout the system lifecycle, or the system is relatively simple (such as a simple multi-tasking control), using compile-time method can reduce code complexity and allow the program to be ready quickly upon startup.
- If tasks are dynamically generated/cancelled in the system (e.g., detecting a new device starts a new task), you can use the dynamic creation method to improve system flexibility.
Summary
- Static (Compile Time) Thread Creation:
- Use
K_THREAD_DEFINE() - Simple, fast, clear program architecture
- Suitable for fixed and few long-term threads
- Use
- Dynamic (Run Time) Thread Creation:
- Use
k_thread_create() - Need to manage stack, control block etc. yourself
- Suitable for application scenarios requiring flexible creation and destruction of large or indefinite number of threads
- Use
