SoFunction
Updated on 2025-04-06

Detailed explanation of common implementation methods of C language thread pool

In C, thread pools usually passpthreadlibrary to implement. The following is a detailed description that introduces common implementations of C thread pools, including core concepts, implementation steps, and specific code examples.

1. Basic structure of thread pool

The core concept of thread pool is to have a fixed number of threads waiting for tasks to be executed. Tasks are usually passed through a task queue, and threads take tasks out of the queue and execute them. The main goal of thread pools is to improve resource utilization and avoid frequent creation and destruction of threads.

The main components of thread pool:

  • Task structure: Save task information, such as function pointers and parameters of the task.
  • Task Queue: Used to store pending tasks. When all worker threads are busy, newly submitted tasks will be placed in the queue until the thread is idle.
  • Thread pool control: manage threads in thread pools, schedule task distribution, and maintain task queues.

2. Implementation steps of thread pool

Here are the basic steps to implement a simple thread pool:

Initialize thread pool

Create a certain number of threads and put them in a waiting state.

Create a task queue to store tasks to be executed.

Task Submission

The user submits a task to the thread pool, and the thread pool will put the task into the task queue and wait for the worker thread to execute.

Worker thread

The worker thread takes the task from the task queue and executes it.

If there are no tasks, the thread will block until a task is submitted to the task queue.

Close the thread pool

When closing the thread pool, you need to ensure that all tasks are completed before destroying the thread pool and freeing all resources.

3. The core data structure of thread pool

Task structure: Each task usually includes the execution function of the task and the parameters of the task.

typedef struct {
    void (*routine)(void *arg);  // Functions for task execution    void *arg;                   // Parameters passed to task function} task_t;

Thread pool structure: The thread pool needs to include task queues, thread arrays, thread counts, locks, and condition variables.

typedef struct {
    pthread_t *threads;           // Worker thread array    task_t *task_queue;           // Task queue    int queue_size;               // Queue size    int head, tail;               // Queue head and tail index    int thread_count;             // Number of threads in thread pool    pthread_mutex_t lock;        // Lock, protect the task queue    pthread_cond_t cond;         // Condition variable, wake up the worker thread    int shutdown;                 // Whether to close the thread pool} thread_pool_t;

4. Detailed implementation of thread pool

Below is a complete thread pool implementation, including initialization, task submission, task execution and destruction.

4.1 Initialize the thread pool

First, you need to create a thread pool and initialize the necessary data structures.

#include <>
#include <>
#include <>
#include <>
typedef struct {
    void (*routine)(void *arg);  // Functions for task execution    void *arg;                   // Parameters passed to task function} task_t;
typedef struct {
    pthread_t *threads;           // Worker thread array    task_t *task_queue;           // Task queue    int queue_size;               // Queue size    int head, tail;               // Queue head and tail index    int thread_count;             // Number of threads in thread pool    pthread_mutex_t lock;        // Lock, protect the task queue    pthread_cond_t cond;         // Condition variable, wake up the worker thread    int shutdown;                 // Whether to close the thread pool} thread_pool_t;
void *worker(void *arg) {
    thread_pool_t *pool = (thread_pool_t *)arg;
    while (1) {
        pthread_mutex_lock(&pool->lock);
        while (pool->head == pool->tail && !pool->shutdown) {
            pthread_cond_wait(&pool->cond, &pool->lock);  // Wait for the task        }
        // Check whether the thread pool is closed        if (pool->shutdown) {
            pthread_mutex_unlock(&pool->lock);
            break;
        }
        task_t task = pool->task_queue[pool->head];  // Get the task        pool->head = (pool->head + 1) % pool->queue_size;  // Remove tasks from the queue        pthread_mutex_unlock(&pool->lock);
        ();  // Execute tasks    }
    pthread_exit(NULL);
}
void thread_pool_init(thread_pool_t *pool, int thread_count, int queue_size) {
    pool->threads = (pthread_t *)malloc(thread_count * sizeof(pthread_t));
    pool->task_queue = (task_t *)malloc(queue_size * sizeof(task_t));
    pool->queue_size = queue_size;
    pool->head = pool->tail = 0;
    pool->thread_count = thread_count;
    pool->shutdown = 0;
    pthread_mutex_init(&pool->lock, NULL);
    pthread_cond_init(&pool->cond, NULL);
    // Create thread    for (int i = 0; i < thread_count; i++) {
        pthread_create(&pool->threads[i], NULL, worker, pool);
    }
}

4.2 Submit a task

When a user needs to execute a task, the task will be added to the task queue and wait for the thread to execute.

void thread_pool_add_task(thread_pool_t *pool, void (*routine)(void *), void *arg) {
    pthread_mutex_lock(&pool->lock);
    // Check whether the task queue is full    if ((pool->tail + 1) % pool->queue_size != pool->head) {
        pool->task_queue[pool->tail].routine = routine;
        pool->task_queue[pool->tail].arg = arg;
        pool->tail = (pool->tail + 1) % pool->queue_size;  // Update the tail of the queue        pthread_cond_signal(&pool->cond);  // Wake up a worker thread    }
    pthread_mutex_unlock(&pool->lock);
}

4.3 Close the thread pool

When closing the thread pool, you need to wait for all threads to complete the task before destroying the thread pool. Can be set byshutdownFlag to notify the thread pool to stop.

void thread_pool_destroy(thread_pool_t *pool) {
    pthread_mutex_lock(&pool->lock);
    pool->shutdown = 1;
    pthread_cond_broadcast(&pool->cond);  // Wake up all threads to ensure that the thread can exit    pthread_mutex_unlock(&pool->lock);
    // Wait for all threads to exit    for (int i = 0; i < pool->thread_count; i++) {
        pthread_join(pool->threads[i], NULL);
    }
    free(pool->threads);
    free(pool->task_queue);
    pthread_mutex_destroy(&pool->lock);
    pthread_cond_destroy(&pool->cond);
}

4.4 Sample task function

Users can define their own task functions, pass parameters, and perform actual work in the task functions.

void print_hello(void *arg) {
    printf("Hello, %s!\n", (char *)arg);
}
int main() {
    thread_pool_t pool;
    thread_pool_init(&pool, 4, 10);  // Create a thread pool containing 4 threads and 10 task queues    for (int i = 0; i < 5; i++) {
        char *name = malloc(10);
        sprintf(name, "Task %d", i + 1);
        thread_pool_add_task(&pool, print_hello, name);
    }
    sleep(1);  // Wait for task execution    thread_pool_destroy(&pool);  // Destroy thread pool    return 0;
}

5. Tuning and optimization of thread pools

In practical applications, the performance of thread pools can be tuned and optimized through the following aspects:

  • Maximum number of threads and minimum number of threads: In order to avoid resource competition caused by excessive threads, you can set the minimum number of threads and the maximum number of threads.
  • Task queue length: The length of the task queue should be moderate. Too long queues may cause over-stacking of tasks, and too short queues may cause the thread pool to fail to fully utilize resources.
  • Dynamic thread adjustment: Dynamically increasing or decreasing the number of threads according to the system load can improve the efficiency and response speed of the system.
  • Task timeout mechanism: In order to prevent some tasks from occupying threads for a long time, the thread pool can set the task timeout mechanism, and give up execution or rescheduling after the task timeout.

Summarize

This article describes how to implement a basic thread pool using C language. The implementation of thread pool includes worker threads, task queues, task scheduling, thread pool initialization, task addition, and destruction. In this way, threads can be effectively managed in multi-tasking and high-concurrency scenarios, reducing the overhead of thread creation and destruction, and improving system efficiency.

This is the end of this article about the common implementation methods of C thread pool. For more related C thread pool content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!