面试题答案
一键面试避免线程饥饿的任务调度
- 公平调度算法:
- 轮询调度(Round - Robin):维护一个任务队列,每个线程按顺序依次从队列中获取任务。例如,创建一个环形队列,线程每次处理完任务后,从队列的下一个位置获取新任务。
// 假设任务结构体 typedef struct Task { void (*func)(void*); void *arg; } Task; Task taskQueue[QUEUE_SIZE]; int head = 0; int tail = 0; Task getTask() { Task task = taskQueue[head]; head = (head + 1) % QUEUE_SIZE; return task; }
- 时间片轮转调度:为每个任务分配一定的执行时间片。当时间片用完后,将任务放回队列末尾等待下次调度。可以使用
setitimer
函数来实现时间片控制。
struct itimerval timer; timer.it_value.tv_sec = 0; timer.it_value.tv_usec = TIME_SLICE_USEC; setitimer(ITIMER_VIRTUAL, &timer, NULL); // 处理时间片到期信号 signal(SIGVTALRM, timeSliceExpiredHandler);
- 优先级调度:
- 为任务设置优先级字段。任务入队时,根据优先级插入到合适的位置。例如,可以使用堆数据结构来维护任务队列,高优先级任务在堆顶。
// 基于堆的优先级队列实现 void insertTask(Task task, int priority) { // 插入任务到堆中合适位置 } Task getHighestPriorityTask() { // 从堆顶获取最高优先级任务 }
- 定期调整任务优先级,避免高优先级任务一直占用资源,导致低优先级任务饥饿。例如,每隔一段时间,将所有任务的优先级降低一定值。
动态调整线程数量时的资源管理
- 文件描述符管理:
- 线程本地存储(TLS):使用
pthread_key_create
创建线程本地存储键,每个线程在需要打开文件描述符时,将其存储在自己的线程本地存储中。
pthread_key_t fileDescriptorKey; pthread_key_create(&fileDescriptorKey, freeFileDescriptor); void* threadFunction(void* arg) { int fd = open("example.txt", O_RDONLY); pthread_setspecific(fileDescriptorKey, (void *)fd); // 线程处理任务 int localFd = (int)pthread_getspecific(fileDescriptorKey); close(localFd); }
- 资源池:创建一个文件描述符资源池,线程从资源池中获取和归还文件描述符。可以使用互斥锁保护资源池的访问。
int fileDescriptorPool[POOL_SIZE]; pthread_mutex_t poolMutex; pthread_mutex_init(&poolMutex, NULL); int getFileDescriptorFromPool() { pthread_mutex_lock(&poolMutex); for (int i = 0; i < POOL_SIZE; i++) { if (fileDescriptorPool[i]!= -1) { int fd = fileDescriptorPool[i]; fileDescriptorPool[i] = -1; pthread_mutex_unlock(&poolMutex); return fd; } } pthread_mutex_unlock(&poolMutex); return -1; // 资源池为空 } void returnFileDescriptorToPool(int fd) { pthread_mutex_lock(&poolMutex); for (int i = 0; i < POOL_SIZE; i++) { if (fileDescriptorPool[i] == -1) { fileDescriptorPool[i] = fd; break; } } pthread_mutex_unlock(&poolMutex); }
- 线程本地存储(TLS):使用
- 内存管理:
- 内存池:创建一个内存池,线程从内存池中分配和释放内存。使用
malloc
和free
管理内存池本身,线程在内存池中分配小块内存。
char *memoryPool = (char *)malloc(POOL_SIZE); int usedBytes = 0; void* allocateFromMemoryPool(size_t size) { if (usedBytes + size <= POOL_SIZE) { void *ptr = memoryPool + usedBytes; usedBytes += size; return ptr; } return NULL; } void freeInMemoryPool(void* ptr) { // 简单实现,这里假设内存池是连续分配且按顺序使用,实际需更复杂逻辑 int offset = (char *)ptr - memoryPool; if (offset >= 0 && offset < usedBytes) { usedBytes -= (usedBytes - offset); } }
- 智能指针(模拟):在任务结构体中,为需要管理的内存分配一个引用计数。当任务完成时,减少引用计数,当引用计数为0时,释放内存。
typedef struct MemoryWrapper { void *ptr; int refCount; } MemoryWrapper; MemoryWrapper* createMemoryWrapper(void *ptr) { MemoryWrapper *wrapper = (MemoryWrapper *)malloc(sizeof(MemoryWrapper)); wrapper->ptr = ptr; wrapper->refCount = 1; return wrapper; } void increaseRefCount(MemoryWrapper *wrapper) { wrapper->refCount++; } void decreaseRefCount(MemoryWrapper *wrapper) { wrapper->refCount--; if (wrapper->refCount == 0) { free(wrapper->ptr); free(wrapper); } }
- 内存池:创建一个内存池,线程从内存池中分配和释放内存。使用