MST

星途 面试题库

面试题:C语言线程池线程生命周期优化与异常处理

在高并发场景下的Linux C语言线程池,当线程在执行任务过程中出现未捕获的异常(如除零错误、内存越界等)时,如何在不影响整个线程池运行的前提下,优雅地处理该线程的生命周期,包括但不限于资源回收、线程重启等操作,同时要尽量减少对其他正常运行线程的性能影响,阐述详细的设计思路和关键代码实现要点。
33.9万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 异常捕获机制:在每个线程执行任务的函数内部,使用setjmplongjmp来捕获异常。setjmp用于设置一个跳转点,longjmp用于跳转到该点。这样,当异常发生时,可以跳转到特定的错误处理代码块。
  2. 资源回收:在捕获到异常后,需要对该线程已经分配但未释放的资源进行回收。这包括动态分配的内存、文件描述符、锁等资源。对于动态内存,使用free函数释放;对于文件描述符,使用close函数关闭;对于锁,使用相应的解锁函数(如pthread_mutex_unlock)。
  3. 线程重启:在完成资源回收后,将该线程设置为可以重新获取任务并执行。可以通过一个标志位表示线程是否处于可用状态,当捕获到异常并完成处理后,将该标志位置为可用。
  4. 性能影响最小化:为了减少对其他正常运行线程的性能影响,异常处理代码应尽量简洁高效。避免在处理异常时持有锁过长时间,防止其他线程等待。同时,资源回收操作也应尽可能快速,例如在回收内存时,避免频繁的系统调用。

关键代码实现要点

  1. 线程函数框架
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;
}
  1. 任务结构定义
typedef struct {
    void (*func)(void*);
    void* arg;
} task_t;
  1. 任务队列操作
// 获取任务函数
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;
}
  1. 线程池初始化与管理
// 线程池初始化
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加入线程数组等操作
    }
}

在上述代码中,通过setjmplongjmp实现异常捕获,在异常处理代码块中进行资源回收,并通过循环继续获取任务,实现线程的重启,同时通过合理的锁操作和简洁的异常处理代码,尽量减少对其他线程的性能影响。