频繁创建和销毁线程对系统资源和性能的影响
- 资源消耗
- 内存:每次创建线程,系统需要为线程栈分配内存空间。默认情况下,Linux线程栈大小一般为8MB(可通过
ulimit -s
查看和修改)。频繁创建销毁线程会导致内存频繁分配和释放,增加内存管理开销,甚至可能导致内存碎片问题,降低内存利用率。
- 内核资源:内核需要为每个线程维护相应的控制块(
task_struct
等相关数据结构),包含线程的状态、寄存器值、调度信息等。频繁创建和销毁线程会使内核频繁进行这些数据结构的创建和销毁操作,消耗内核资源。
- 性能影响
- 调度开销:线程的创建和销毁会引起系统调度。创建新线程时,内核需要将其纳入调度队列;销毁线程时,要从调度队列中移除。这会增加调度器的负担,尤其在高并发场景下,大量的调度操作会降低系统整体性能。
- 上下文切换开销:每次创建新线程并调度运行,以及销毁线程前进行上下文切换,都需要保存和恢复寄存器值、栈指针等信息。频繁的上下文切换会导致CPU时间浪费在这些操作上,降低实际用于执行用户代码的时间。
优化方案
- 线程池技术
- 原理:预先创建一定数量的线程,放入线程池中。当有任务需要处理时,从线程池中获取一个空闲线程来执行任务,任务完成后,线程不销毁,而是返回线程池等待下一个任务。这样避免了频繁的线程创建和销毁操作。
- 涉及的系统调用:
pthread_create
:用于在初始化线程池时创建固定数量的线程。例如:
#include <pthread.h>
#include <stdio.h>
#define THREAD_POOL_SIZE 5
void* worker(void* arg) {
// 线程执行的任务
printf("Worker thread %ld is running\n", pthread_self());
return NULL;
}
int main() {
pthread_t threads[THREAD_POOL_SIZE];
for (int i = 0; i < THREAD_POOL_SIZE; i++) {
if (pthread_create(&threads[i], NULL, worker, NULL) != 0) {
perror("pthread_create");
return 1;
}
}
// 等待所有线程结束
for (int i = 0; i < THREAD_POOL_SIZE; i++) {
pthread_join(threads[i], NULL);
}
return 0;
}
- **`pthread_join`**:在主线程中等待线程池中的线程完成任务。如上述代码中,主线程通过`pthread_join`等待每个线程结束。
- 代码实现思路:
- 数据结构设计:
- 设计一个任务队列,用于存放待处理的任务。可以使用链表或数组实现,例如使用链表:
typedef struct Task {
void* (*func)(void*);
void* arg;
struct Task* next;
} Task;
typedef struct TaskQueue {
Task* head;
Task* tail;
} TaskQueue;
- 设计一个线程池结构体,包含线程数组、任务队列、线程池状态等信息:
typedef struct ThreadPool {
pthread_t* threads;
TaskQueue taskQueue;
int isShutdown;
pthread_mutex_t mutex;
pthread_cond_t cond;
} ThreadPool;
- **初始化线程池**:
- 分配线程数组和初始化任务队列。
- 初始化互斥锁`pthread_mutex_init`和条件变量`pthread_cond_init`。
- 创建固定数量的线程,线程函数为从任务队列中获取任务并执行。
ThreadPool* createThreadPool(int numThreads) {
ThreadPool* pool = (ThreadPool*)malloc(sizeof(ThreadPool));
pool->threads = (pthread_t*)malloc(numThreads * sizeof(pthread_t));
pool->taskQueue.head = pool->taskQueue.tail = NULL;
pool->isShutdown = 0;
pthread_mutex_init(&pool->mutex, NULL);
pthread_cond_init(&pool->cond, NULL);
for (int i = 0; i < numThreads; i++) {
pthread_create(&pool->threads[i], NULL, workerThread, pool);
}
return pool;
}
- **任务入队**:
- 加锁,将任务添加到任务队列尾部。
- 解锁后,通知条件变量有新任务。
void addTask(ThreadPool* pool, void* (*func)(void*), void* arg) {
Task* newTask = (Task*)malloc(sizeof(Task));
newTask->func = func;
newTask->arg = arg;
newTask->next = NULL;
pthread_mutex_lock(&pool->mutex);
if (pool->taskQueue.tail == NULL) {
pool->taskQueue.head = pool->taskQueue.tail = newTask;
} else {
pool->taskQueue.tail->next = newTask;
pool->taskQueue.tail = newTask;
}
pthread_cond_signal(&pool->cond);
pthread_mutex_unlock(&pool->mutex);
}
- **工作线程函数**:
- 不断从任务队列中获取任务,加锁获取任务,解锁后执行任务。
- 如果任务队列为空且线程池未关闭,则等待条件变量通知。
void* workerThread(void* arg) {
ThreadPool* pool = (ThreadPool*)arg;
while (1) {
Task* task = NULL;
pthread_mutex_lock(&pool->mutex);
while (pool->taskQueue.head == NULL &&!pool->isShutdown) {
pthread_cond_wait(&pool->cond, &pool->mutex);
}
if (pool->isShutdown && pool->taskQueue.head == NULL) {
pthread_mutex_unlock(&pool->mutex);
pthread_exit(NULL);
}
task = pool->taskQueue.head;
pool->taskQueue.head = task->next;
if (pool->taskQueue.head == NULL) {
pool->taskQueue.tail = NULL;
}
pthread_mutex_unlock(&pool->mutex);
task->func(task->arg);
free(task);
}
return NULL;
}
- **销毁线程池**:
- 设置线程池关闭标志。
- 通知所有等待的线程,有任务可执行(实际是通知线程池关闭)。
- 等待所有线程结束,释放资源。
void destroyThreadPool(ThreadPool* pool) {
pthread_mutex_lock(&pool->mutex);
pool->isShutdown = 1;
pthread_cond_broadcast(&pool->cond);
pthread_mutex_unlock(&pool->mutex);
for (int i = 0; i < THREAD_POOL_SIZE; i++) {
pthread_join(pool->threads[i], NULL);
}
free(pool->threads);
Task* task;
while (pool->taskQueue.head != NULL) {
task = pool->taskQueue.head;
pool->taskQueue.head = task->next;
free(task);
}
pthread_mutex_destroy(&pool->mutex);
pthread_cond_destroy(&pool->cond);
free(pool);
}