面试题答案
一键面试设计思路
- 异常捕获机制:在每个线程执行任务的函数内部,使用
setjmp
和longjmp
来捕获异常。setjmp
用于设置一个跳转点,longjmp
用于跳转到该点。这样,当异常发生时,可以跳转到特定的错误处理代码块。 - 资源回收:在捕获到异常后,需要对该线程已经分配但未释放的资源进行回收。这包括动态分配的内存、文件描述符、锁等资源。对于动态内存,使用
free
函数释放;对于文件描述符,使用close
函数关闭;对于锁,使用相应的解锁函数(如pthread_mutex_unlock
)。 - 线程重启:在完成资源回收后,将该线程设置为可以重新获取任务并执行。可以通过一个标志位表示线程是否处于可用状态,当捕获到异常并完成处理后,将该标志位置为可用。
- 性能影响最小化:为了减少对其他正常运行线程的性能影响,异常处理代码应尽量简洁高效。避免在处理异常时持有锁过长时间,防止其他线程等待。同时,资源回收操作也应尽可能快速,例如在回收内存时,避免频繁的系统调用。
关键代码实现要点
- 线程函数框架:
void* thread_function(void* arg) {
jmp_buf env;
if (setjmp(env) != 0) {
// 异常处理,资源回收
// 释放动态分配的内存
// 关闭文件描述符
// 解锁锁等
// 将线程设置为可用状态
return NULL;
}
while (1) {
// 从任务队列获取任务
task_t* task = get_task();
if (task == NULL) {
break;
}
// 执行任务,可能会抛出异常
if (setjmp(env) != 0) {
// 异常处理,资源回收
// 释放任务相关资源
free(task);
continue;
}
task->func(task->arg);
// 任务执行完成,释放任务相关资源
free(task);
}
return NULL;
}
- 任务结构定义:
typedef struct {
void (*func)(void*);
void* arg;
} task_t;
- 任务队列操作:
// 获取任务函数
task_t* get_task() {
// 实现从任务队列获取任务逻辑
// 可能涉及锁操作等
// 例如:
// pthread_mutex_lock(&task_queue_mutex);
// task_t* task = queue_pop(&task_queue);
// pthread_mutex_unlock(&task_queue_mutex);
// return task;
}
- 线程池初始化与管理:
// 线程池初始化
void init_thread_pool(int num_threads) {
// 初始化任务队列
// 初始化锁等
// 创建线程
for (int i = 0; i < num_threads; i++) {
pthread_t tid;
pthread_create(&tid, NULL, thread_function, NULL);
// 将线程ID加入线程数组等操作
}
}
在上述代码中,通过setjmp
和longjmp
实现异常捕获,在异常处理代码块中进行资源回收,并通过循环继续获取任务,实现线程的重启,同时通过合理的锁操作和简洁的异常处理代码,尽量减少对其他线程的性能影响。