MST

星途 面试题库

面试题:C语言线程池任务动态分配的高并发优化

在高并发场景下,Linux C语言线程池的任务动态分配可能会面临性能瓶颈,比如频繁的锁竞争导致线程阻塞。假设现有线程池框架采用互斥锁来保护任务队列。请深入分析可能出现性能问题的地方,并提出至少两种优化方案,用C语言代码示例说明如何对关键部分进行优化,同时阐述每种方案在不同负载情况下的优缺点。
39.6万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

性能问题分析

  1. 锁竞争:多个线程同时访问任务队列时,频繁获取和释放互斥锁会导致锁竞争,线程等待锁的时间增加,降低整体效率。
  2. 线程阻塞:当某个线程持有锁时,其他线程需要等待,可能造成线程长时间阻塞,无法及时处理任务。
  3. 上下文切换:频繁的线程阻塞和唤醒会导致大量的上下文切换开销,消耗系统资源。

优化方案

  1. 减少锁粒度
    • 方案描述:将任务队列按照一定规则(如任务类型、任务ID等)进行划分,每个子队列使用单独的互斥锁。这样不同线程可以同时访问不同子队列,减少锁竞争。
    • C语言代码示例
#include <pthread.h>
#include <stdio.h>

#define QUEUE_SIZE 10
#define SUB_QUEUE_COUNT 2

typedef struct {
    int data[QUEUE_SIZE];
    int head;
    int tail;
    pthread_mutex_t lock;
} SubQueue;

typedef struct {
    SubQueue subQueues[SUB_QUEUE_COUNT];
} TaskQueue;

TaskQueue taskQueue;

void initTaskQueue() {
    for (int i = 0; i < SUB_QUEUE_COUNT; i++) {
        taskQueue.subQueues[i].head = 0;
        taskQueue.subQueues[i].tail = 0;
        pthread_mutex_init(&taskQueue.subQueues[i].lock, NULL);
    }
}

void enqueue(int task, int subQueueIndex) {
    if (subQueueIndex < 0 || subQueueIndex >= SUB_QUEUE_COUNT) {
        return;
    }
    pthread_mutex_lock(&taskQueue.subQueues[subQueueIndex].lock);
    taskQueue.subQueues[subQueueIndex].data[taskQueue.subQueues[subQueueIndex].tail] = task;
    taskQueue.subQueues[subQueueIndex].tail = (taskQueue.subQueues[subQueueIndex].tail + 1) % QUEUE_SIZE;
    pthread_mutex_unlock(&taskQueue.subQueues[subQueueIndex].lock);
}

int dequeue(int subQueueIndex) {
    if (subQueueIndex < 0 || subQueueIndex >= SUB_QUEUE_COUNT) {
        return -1;
    }
    pthread_mutex_lock(&taskQueue.subQueues[subQueueIndex].lock);
    if (taskQueue.subQueues[subQueueIndex].head == taskQueue.subQueues[subQueueIndex].tail) {
        pthread_mutex_unlock(&taskQueue.subQueues[subQueueIndex].lock);
        return -1;
    }
    int task = taskQueue.subQueues[subQueueIndex].data[taskQueue.subQueues[subQueueIndex].head];
    taskQueue.subQueues[subQueueIndex].head = (taskQueue.subQueues[subQueueIndex].head + 1) % QUEUE_SIZE;
    pthread_mutex_unlock(&taskQueue.subQueues[subQueueIndex].lock);
    return task;
}
- **优缺点**:
    - **优点**:在高并发且任务分布较为均匀的情况下,能显著减少锁竞争,提高性能。因为不同线程可以并行处理不同子队列的任务。
    - **缺点**:如果任务分布不均匀,某个子队列可能会成为热点,仍然存在锁竞争问题。同时,管理多个锁增加了代码复杂度。

2. 无锁数据结构 - 方案描述:使用无锁数据结构,如无锁队列(如基于CAS操作实现的队列)。无锁数据结构允许线程在不使用锁的情况下安全地访问和修改数据,避免锁竞争。 - C语言代码示例:(简单模拟基于CAS的无锁队列)

#include <pthread.h>
#include <stdio.h>
#include <stdatomic.h>

#define QUEUE_SIZE 10

typedef struct {
    atomic_int head;
    atomic_int tail;
    int data[QUEUE_SIZE];
} LockFreeQueue;

LockFreeQueue lockFreeQueue;

void initLockFreeQueue() {
    atomic_store(&lockFreeQueue.head, 0);
    atomic_store(&lockFreeQueue.tail, 0);
}

int enqueueLockFree(int task) {
    int tail = atomic_load(&lockFreeQueue.tail);
    int nextTail = (tail + 1) % QUEUE_SIZE;
    if (nextTail == atomic_load(&lockFreeQueue.head)) {
        return 0; // 队列满
    }
    lockFreeQueue.data[tail] = task;
    atomic_store(&lockFreeQueue.tail, nextTail);
    return 1;
}

int dequeueLockFree() {
    int head = atomic_load(&lockFreeQueue.head);
    if (head == atomic_load(&lockFreeQueue.tail)) {
        return -1; // 队列为空
    }
    int task = lockFreeQueue.data[head];
    atomic_store(&lockFreeQueue.head, (head + 1) % QUEUE_SIZE);
    return task;
}
- **优缺点**:
    - **优点**:在高并发场景下,能提供极高的性能,避免了锁竞争和线程阻塞带来的开销。适用于对性能要求极高的场景。
    - **缺点**:实现复杂,调试困难。无锁数据结构通常依赖底层原子操作,代码可读性较差。并且在某些情况下,可能会因为频繁的CAS重试导致CPU资源浪费。