Task pooling helps manage many small jobs efficiently by reusing a fixed number of tasks instead of creating and deleting tasks repeatedly.
Task pooling for dynamic workloads in FreeRTOS
/* Create a fixed number of worker tasks */ for (int i = 0; i < POOL_SIZE; i++) { xTaskCreate(workerTask, "Worker", STACK_SIZE, NULL, PRIORITY, &taskHandles[i]); } /* Use a queue to send work items to the pool */ QueueHandle_t workQueue = xQueueCreate(QUEUE_LENGTH, sizeof(WorkItem)); /* Worker task function */ void workerTask(void *params) { WorkItem item; while (1) { if (xQueueReceive(workQueue, &item, portMAX_DELAY) == pdPASS) { processWorkItem(&item); } } }
Use a queue to send work items to the pool of tasks.
Worker tasks wait on the queue and process items as they arrive.
/* Define work item structure */ typedef struct { int jobId; char data[20]; } WorkItem;
/* Create work queue and tasks */ workQueue = xQueueCreate(10, sizeof(WorkItem)); for (int i = 0; i < 3; i++) { xTaskCreate(workerTask, "Worker", 1000, NULL, 2, NULL); }
/* Send work to the queue */ WorkItem newJob = {1, "Task data"}; xQueueSend(workQueue, &newJob, 0);
This program creates a pool of 2 worker tasks and a queue. It sends 3 jobs to the queue. Each worker waits for jobs and processes them by printing the job info.
#include "FreeRTOS.h" #include "task.h" #include "queue.h" #include <stdio.h> #define POOL_SIZE 2 #define STACK_SIZE 1000 #define PRIORITY 2 typedef struct { int jobId; char data[20]; } WorkItem; QueueHandle_t workQueue; void processWorkItem(WorkItem *item) { printf("Processing job %d with data: %s\n", item->jobId, item->data); vTaskDelay(pdMS_TO_TICKS(500)); // Simulate work } void workerTask(void *params) { WorkItem item; while (1) { if (xQueueReceive(workQueue, &item, portMAX_DELAY) == pdPASS) { processWorkItem(&item); } } } int main(void) { workQueue = xQueueCreate(5, sizeof(WorkItem)); for (int i = 0; i < POOL_SIZE; i++) { xTaskCreate(workerTask, "Worker", STACK_SIZE, NULL, PRIORITY, NULL); } WorkItem jobs[] = { {1, "Data A"}, {2, "Data B"}, {3, "Data C"} }; for (int i = 0; i < 3; i++) { xQueueSend(workQueue, &jobs[i], 0); } vTaskStartScheduler(); return 0; }
Make sure the queue size matches the expected workload to avoid lost jobs.
Worker tasks run forever, so they should be designed to handle all jobs safely.
Use vTaskDelay or blocking calls to avoid busy waiting and save CPU.
Task pooling reuses a fixed number of tasks to handle many jobs efficiently.
Use a queue to send work items to the pool of worker tasks.
This approach saves memory and improves responsiveness for dynamic workloads.
Practice
Solution
Step 1: Understand task pooling concept
Task pooling means reusing a limited number of tasks instead of creating new ones for each job.Step 2: Identify benefits of task pooling
This reuse saves memory and improves efficiency by avoiding overhead of task creation and deletion.Final Answer:
It reuses a fixed number of tasks to handle many jobs efficiently. -> Option AQuick Check:
Task pooling = reuse fixed tasks [OK]
- Thinking task pooling creates a new task per job
- Believing task pooling removes the need for queues
- Assuming task pooling uses more memory
Solution
Step 1: Identify communication method for task pools
Task pools receive work items via queues to distribute jobs among tasks.Step 2: Match API to queue sending
xQueueSend() is the FreeRTOS function to send data to a queue.Final Answer:
xQueueSend() -> Option BQuick Check:
Send work to queue = xQueueSend() [OK]
- Confusing delay or semaphore functions with queue sending
- Using vTaskDelete() which deletes tasks, not send work
void workerTask(void *params) {
int workItem;
while (xQueueReceive(workQueue, &workItem, portMAX_DELAY)) {
printf("Processing %d\n", workItem);
}
}
// Main code
int main() {
int item = 42;
xQueueSend(workQueue, &item, 0);
vTaskStartScheduler();
return 0;
}Solution
Step 1: Check queue usage in code
The code uses workQueue but does not show it being created or initialized.Step 2: Understand FreeRTOS queue requirements
Queues must be created with xQueueCreate before use; missing this causes compile or runtime errors.Final Answer:
Compilation error due to missing queue creation -> Option DQuick Check:
Queue must be created before use [OK]
- Assuming queue exists without creation
- Expecting output without scheduler running properly
- Ignoring queue initialization
void workerTask(void *params) {
int workItem;
while (xQueueReceive(workQueue, &workItem, 0)) {
process(workItem);
}
}Solution
Step 1: Analyze xQueueReceive timeout parameter
Timeout of 0 means non-blocking call; if queue empty, returns immediately causing loop to run continuously.Step 2: Understand impact on CPU usage
This busy loop wastes CPU cycles, leading to high CPU usage and inefficient task pooling.Final Answer:
Using zero timeout causes busy waiting and high CPU usage. -> Option AQuick Check:
Zero timeout = busy wait [OK]
- Thinking zero timeout is safe for task pools
- Confusing pointer types for queue data
- Assuming process() is FreeRTOS API
Solution
Step 1: Understand efficient task pooling
Efficient pooling uses fixed tasks that wait (block) for work to avoid wasting CPU.Step 2: Evaluate options for workload handling
Blocking on xQueueReceive with portMAX_DELAY lets tasks sleep until work arrives, saving CPU and memory.Final Answer:
Create 3 tasks that block on xQueueReceive with portMAX_DELAY and send work via a queue. -> Option CQuick Check:
Block on queue = efficient task pool [OK]
- Polling queue wastes CPU
- Creating tasks dynamically wastes memory
- Using single task delays reduces responsiveness
Ready to prove it?
Pass the test and this pattern moves unknown
