In C, thread pools usually passpthread
library 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 byshutdown
Flag 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!