Skip to main content

C-Thread-Pool: A Lightweight Thread Pool Library for Embedded Systems

·1238 words·6 mins
Embedded Systems Thread Pool Multithreading C Programming Posix Embedded Linux Concurrency Performance RTOS
Table of Contents

C-Thread-Pool: A Lightweight Thread Pool Library for Embedded Systems

Multithreading is one of the most effective ways to improve responsiveness and throughput in embedded applications. Whether processing network packets, collecting sensor data, or handling asynchronous events, multiple worker threads can significantly increase overall system efficiency.

However, repeatedly creating and destroying threads is expensive. Thread initialization, scheduling, stack allocation, and cleanup all consume CPU time and memory, making frequent thread creation a performance bottleneck in many embedded systems.

A thread pool solves this problem by creating a fixed number of worker threads in advance and reusing them to execute incoming tasks. This approach minimizes overhead, improves responsiveness, and simplifies task management.

In this article, we’ll introduce C-Thread-Pool, a lightweight ANSI C thread pool implementation that is ideal for embedded Linux applications.


What Is a Thread Pool?
#

A thread pool is a collection of worker threads that remain alive throughout an application’s lifetime. Instead of creating a new thread for every task, work items are placed into a queue, and idle worker threads continuously fetch and execute them.

This design greatly reduces the cost of thread management while enabling efficient parallel execution.

Why Use a Thread Pool?
#

Thread Reuse
#

Creating and destroying threads repeatedly incurs unnecessary overhead.

If:

  • T1 = Thread creation time
  • T2 = Task execution time
  • T3 = Thread destruction time

then executing every task individually requires:

Ttotal = T1 + T2 + T3

With a thread pool, only the task execution time remains:

Ttotal ≈ T2

Since worker threads already exist, creation and destruction costs are effectively eliminated.

Resource Control
#

A thread pool limits the maximum number of concurrent threads.

Benefits include:

  • Reduced memory consumption
  • Fewer context switches
  • More predictable CPU utilization
  • Better system stability

This is especially important on embedded devices with limited RAM and CPU resources.

Simplified Thread Management
#

Most thread pool implementations provide configurable options such as:

  • Number of worker threads
  • Task queue size
  • Idle timeout
  • Pause/resume functionality
  • Waiting for all tasks to complete

These features make concurrent programming much easier than manually managing individual threads.


Introducing C-Thread-Pool
#

C-Thread-Pool is a lightweight, open-source thread pool library written entirely in ANSI C.

GitHub:

https://github.com/Pithikos/C-Thread-Pool

License:

  • MIT License

Key Features
#

  • ANSI C compliant
  • POSIX pthread compatible
  • Lightweight implementation
  • Easy-to-understand API
  • Supports:
    • Pause
    • Resume
    • Wait
  • Well tested
  • Simple integration into existing projects

Unlike many larger concurrency frameworks, C-Thread-Pool consists of only a few source files and can be compiled directly into your project.


Building the Library
#

The library is distributed as source code rather than a precompiled library.

Compile it together with your application:

gcc example.c thpool.c -pthread -o example

For additional debugging information:

gcc example.c thpool.c -DTHPOOL_DEBUG -pthread -o example

The -pthread option enables POSIX thread support during both compilation and linking.


Basic Usage
#

Using C-Thread-Pool only requires three steps.

1. Include the Header
#

#include "thpool.h"

2. Create a Thread Pool
#

For example, create four worker threads:

threadpool thpool = thpool_init(4);

The threads are created immediately and begin waiting for incoming work.


3. Submit Tasks
#

Each task consists of:

  • A function
  • A single argument pointer
thpool_add_work(thpool, task_function, task_argument);

Worker threads automatically retrieve tasks from the queue and execute them.


Example: Parallel Data Processing
#

The following example computes the square of ten integers concurrently.

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include "thpool.h"

typedef struct
{
    int *data;
    int index;
    long result;
} test_data_t;

void task(void *arg)
{
    test_data_t *test_data = (test_data_t *)arg;

    test_data->result =
        (long)test_data->data[test_data->index] *
        test_data->data[test_data->index];

    printf("Thread %lu result = %ld\n",
           (unsigned long)pthread_self(),
           test_data->result);

    free(test_data);
}

int main(void)
{
    int data[] = {1,2,3,4,5,6,7,8,9,10};

    int count = sizeof(data)/sizeof(data[0]);

    threadpool pool = thpool_init(4);

    for (int i = 0; i < count; i++)
    {
        test_data_t *work = malloc(sizeof(test_data_t));

        work->data = data;
        work->index = i;

        thpool_add_work(pool, task, work);
    }

    thpool_wait(pool);

    puts("Destroying thread pool");

    thpool_destroy(pool);

    return 0;
}

Example Output
#

Running the example produces output similar to:

THPOOL_DEBUG: Created thread 0
THPOOL_DEBUG: Created thread 1
THPOOL_DEBUG: Created thread 2
THPOOL_DEBUG: Created thread 3

Thread 140415592793856 result = 1
Thread 140415584401152 result = 4
Thread 140415576008448 result = 9
Thread 140415567615744 result = 16
Thread 140415592793856 result = 25
Thread 140415584401152 result = 36
Thread 140415576008448 result = 49
Thread 140415567615744 result = 64
Thread 140415592793856 result = 81
Thread 140415584401152 result = 100

Destroying thread pool

Notice that the same worker threads execute multiple tasks, demonstrating thread reuse.


Understanding the Execution Flow
#

The workflow of a thread pool can be visualized as follows:

             +----------------------+
             |   Create Thread Pool |
             +----------+-----------+
                        |
                        v
             +----------------------+
             | Worker Threads Sleep |
             +----------+-----------+
                        |
              New Task Arrives
                        |
                        v
             +----------------------+
             |    Push Into Queue   |
             +----------+-----------+
                        |
                        v
             +----------------------+
             | Worker Takes a Task  |
             +----------+-----------+
                        |
                        v
             +----------------------+
             | Execute Task         |
             +----------+-----------+
                        |
                        v
             +----------------------+
             | Return to Idle State |
             +----------------------+

Unlike traditional thread creation, workers never terminate after completing a task—they simply wait for the next one.


Embedded System Applications
#

Thread pools are useful across many embedded workloads.

Network Servers
#

Each client request becomes an independent task.

Instead of spawning a thread for every connection, requests are submitted to the pool.

Benefits include:

  • Lower latency
  • Better scalability
  • Reduced CPU overhead
  • Improved throughput

Data Processing
#

Large datasets can be divided into smaller pieces.

Examples include:

  • Image processing
  • Signal processing
  • Data compression
  • Searching
  • Sorting

Each chunk can be processed simultaneously by a different worker thread, greatly reducing total execution time.


Sensor Data Acquisition
#

Embedded devices often collect data from multiple sensors simultaneously.

A thread pool can:

  • Receive sensor readings
  • Perform filtering
  • Execute preprocessing
  • Store results
  • Trigger alarms

without repeatedly creating new threads.


Real-Time Systems
#

Although hard real-time systems often rely on RTOS task schedulers, thread pools can still be valuable for Linux-based real-time applications.

Typical workloads include:

  • Camera processing
  • Audio pipelines
  • Industrial monitoring
  • Edge AI preprocessing
  • Communication gateways

Since worker threads are already running, task dispatch latency is significantly reduced.


Advantages
#

C-Thread-Pool offers several practical advantages for embedded applications.

  • Very small codebase
  • Easy integration
  • Minimal dependencies
  • Good performance
  • Portable across POSIX platforms
  • Open-source MIT license
  • Straightforward API
  • Well suited for embedded Linux

For many projects, it provides nearly everything needed without introducing unnecessary complexity.


Limitations
#

Like any thread pool, C-Thread-Pool also has some limitations.

  • POSIX-only (requires pthreads)
  • Not suitable for bare-metal microcontrollers
  • Long-running tasks can occupy workers and delay queued tasks
  • Queue management still requires synchronization overhead

Selecting an appropriate pool size is also important. Too few threads reduce concurrency, while too many increase context switching and memory usage.


Best Practices
#

When using a thread pool in embedded systems:

  • Keep individual tasks short.
  • Avoid blocking operations whenever possible.
  • Minimize lock contention.
  • Allocate worker count according to CPU cores.
  • Free dynamically allocated task data inside the worker.
  • Wait for all pending work before destroying the pool.

Following these practices helps maximize throughput while maintaining predictable system behavior.


Conclusion
#

Thread pools are one of the most effective techniques for improving performance in multithreaded embedded applications. By eliminating repeated thread creation and destruction, they reduce CPU overhead, improve responsiveness, and provide better control over system resources.

For embedded Linux projects that rely on POSIX threads, C-Thread-Pool offers a lightweight, easy-to-integrate solution that delivers efficient parallel task execution without unnecessary complexity.

Whether you’re building network servers, industrial controllers, edge AI gateways, or data acquisition systems, a well-designed thread pool can significantly improve both performance and scalability while keeping your codebase clean and maintainable.

Related

Optimizing Multithreaded Performance: Avoiding False Sharing
·659 words·4 mins
Multithreading Performance Cache False Sharing Pthread
Automotive AI RTOS Comparison: QNX vs FreeRTOS vs Zephyr
·1247 words·6 mins
QNX FreeRTOS Zephyr Automotive RTOS Embedded AI AUTOSAR Machine Learning Embedded Systems ADAS
UNIX Network Programming Volume 2: IPC Mastery Guide
·1280 words·7 mins
UNIX Linux IPC Systems Programming Posix System V Concurrency Network Programming