MST

星途 面试题库

面试题:C语言Linux线程创建与终止的高级问题及优化

在高并发场景下,大量使用C语言在Linux中创建和终止线程,可能会遇到哪些性能瓶颈和资源管理问题?如何通过优化线程创建与终止机制、资源分配策略等来解决这些问题?请深入分析并结合代码示例说明。
14.3万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

性能瓶颈和资源管理问题

  1. 性能瓶颈
    • 线程创建开销:线程创建涉及内核态与用户态的切换,需要分配内核资源,如线程控制块(TCB)等,频繁创建线程会导致系统开销增大。
    • 线程终止开销:线程终止同样需要内核处理,释放相关资源,频繁终止也会带来额外开销。
    • 调度开销:大量线程竞争CPU资源,增加了调度器的工作负担,导致CPU在上下文切换上花费更多时间,降低了实际执行任务的时间占比。
  2. 资源管理问题
    • 文件描述符泄漏:如果在线程中打开了文件描述符,但在线程终止时没有正确关闭,会导致文件描述符泄漏,最终耗尽系统资源。
    • 内存泄漏:线程中动态分配的内存,如果在线程终止时没有释放,会造成内存泄漏。
    • 锁争用:多个线程可能竞争同一资源,使用锁进行同步时,如果设计不当,会出现锁争用问题,降低系统并发性能。

优化措施

  1. 优化线程创建与终止机制
    • 线程池:提前创建一定数量的线程并放入线程池,任务来时直接从线程池获取线程执行,任务完成后线程不终止而是返回线程池等待下一个任务。这样减少了线程创建和终止的开销。
    • 延迟线程终止:对于一些需要频繁创建和终止的线程,可以采用延迟终止策略,即线程执行完任务后不立即终止,而是等待一段时间,看是否有新任务到来,避免反复创建和销毁。
  2. 资源分配策略
    • 资源预分配:在线程池创建时,预先分配一些常用资源,如文件描述符、内存等,线程从线程池获取资源,使用完后归还,减少资源动态分配和释放的开销。
    • 资源复用:对于一些可复用的资源,如数据库连接等,在线程间共享使用,避免每个线程都创建新的资源。

代码示例

以下是一个简单的线程池示例代码,使用C语言和POSIX线程库:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

#define THREAD_POOL_SIZE 5
#define TASK_QUEUE_SIZE 10

// 任务结构体
typedef struct Task {
    void (*func)(void*);
    void *arg;
    struct Task *next;
} Task;

// 线程池结构体
typedef struct ThreadPool {
    pthread_t threads[THREAD_POOL_SIZE];
    Task *task_queue_head;
    Task *task_queue_tail;
    int task_queue_count;
    pthread_mutex_t queue_mutex;
    pthread_cond_t queue_cond;
    int stop;
} ThreadPool;

// 初始化线程池
void initThreadPool(ThreadPool *pool) {
    pool->task_queue_head = NULL;
    pool->task_queue_tail = NULL;
    pool->task_queue_count = 0;
    pool->stop = 0;

    pthread_mutex_init(&pool->queue_mutex, NULL);
    pthread_cond_init(&pool->queue_cond, NULL);

    for (int i = 0; i < THREAD_POOL_SIZE; i++) {
        pthread_create(&pool->threads[i], NULL, (void* (*)(void*))[](void* arg) {
            ThreadPool *pool = (ThreadPool *)arg;
            while (1) {
                Task *task = NULL;
                pthread_mutex_lock(&pool->queue_mutex);
                while (pool->task_queue_count == 0 &&!pool->stop) {
                    pthread_cond_wait(&pool->queue_cond, &pool->queue_mutex);
                }
                if (pool->stop && pool->task_queue_count == 0) {
                    pthread_mutex_unlock(&pool->queue_mutex);
                    pthread_exit(NULL);
                }
                task = pool->task_queue_head;
                pool->task_queue_head = task->next;
                if (pool->task_queue_head == NULL) {
                    pool->task_queue_tail = NULL;
                }
                pool->task_queue_count--;
                pthread_mutex_unlock(&pool->queue_mutex);

                (*task->func)(task->arg);
                free(task);
            }
        }, pool);
    }
}

// 添加任务到线程池
void addTask(ThreadPool *pool, void (*func)(void*), void *arg) {
    Task *task = (Task *)malloc(sizeof(Task));
    task->func = func;
    task->arg = arg;
    task->next = NULL;

    pthread_mutex_lock(&pool->queue_mutex);
    if (pool->task_queue_tail == NULL) {
        pool->task_queue_head = task;
        pool->task_queue_tail = task;
    } else {
        pool->task_queue_tail->next = task;
        pool->task_queue_tail = task;
    }
    pool->task_queue_count++;
    pthread_cond_signal(&pool->queue_cond);
    pthread_mutex_unlock(&pool->queue_mutex);
}

// 销毁线程池
void destroyThreadPool(ThreadPool *pool) {
    pthread_mutex_lock(&pool->queue_mutex);
    pool->stop = 1;
    pthread_cond_broadcast(&pool->queue_cond);
    pthread_mutex_unlock(&pool->queue_mutex);

    for (int i = 0; i < THREAD_POOL_SIZE; i++) {
        pthread_join(pool->threads[i], NULL);
    }

    pthread_mutex_destroy(&pool->queue_mutex);
    pthread_cond_destroy(&pool->queue_cond);
}

// 示例任务函数
void exampleTask(void *arg) {
    printf("Task executed with argument: %d\n", *((int *)arg));
    sleep(1);
}

int main() {
    ThreadPool pool;
    initThreadPool(&pool);

    int args[5] = {1, 2, 3, 4, 5};
    for (int i = 0; i < 5; i++) {
        addTask(&pool, exampleTask, &args[i]);
    }

    sleep(3);
    destroyThreadPool(&pool);

    return 0;
}

在这个示例中,通过线程池机制,减少了线程创建和终止的开销,同时通过合理的资源管理(如任务结构体的动态分配和释放),避免了内存泄漏等问题。