面试题答案
一键面试可能的原因分析
- 竞争激烈:高并发场景下,多个线程或进程频繁竞争信号量,导致大量时间消耗在信号量的等待和获取上。
- 系统调用开销:信号量操作往往涉及系统调用,频繁的系统调用会增加内核与用户空间切换的开销,降低性能。
优化方案
- 减少信号量使用:分析资源访问逻辑,尽量采用无锁数据结构或乐观锁机制,减少对信号量的依赖,从而降低竞争。例如,对于一些读多写少的场景,可以使用读写锁(
pthread_rwlock
)代替普通信号量,允许多个读操作并发执行。 - 优化信号量粒度:将大粒度的信号量拆分成多个小粒度信号量。比如在管理一组资源时,为每个资源或部分资源分配单独的信号量,这样不同线程对不同资源的访问可以并行进行,减少整体的竞争。
- 使用更高效的同步原语:在某些场景下,条件变量(
pthread_cond
)结合互斥锁(pthread_mutex
)可能比信号量更高效。条件变量可以在特定条件满足时通知等待的线程,避免了信号量可能出现的不必要等待和唤醒。
处理系统调用被中断(EINTR
错误)
当信号量操作(如sem_wait
等系统调用)遇到EINTR
错误时,应按照以下方式正确处理以保证程序健壮性:
- 重试系统调用:在捕获到
EINTR
错误后,重新调用被中断的系统调用。例如:
#include <semaphore.h>
#include <stdio.h>
int main() {
sem_t sem;
sem_init(&sem, 0, 1);
int ret;
do {
ret = sem_wait(&sem);
} while (ret == -1 && errno == EINTR);
if (ret == -1) {
perror("sem_wait error");
return 1;
}
// 临界区代码
sem_post(&sem);
sem_destroy(&sem);
return 0;
}
- 设置信号处理函数:合理设置信号处理函数,避免不必要的信号干扰。例如,对于可能导致系统调用中断的信号,在关键的信号量操作期间,可以暂时屏蔽该信号,操作完成后再恢复信号处理。
#include <semaphore.h>
#include <signal.h>
#include <stdio.h>
void signal_handler(int signum) {
// 信号处理逻辑
}
int main() {
sem_t sem;
sem_init(&sem, 0, 1);
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGINT);
sigprocmask(SIG_BLOCK, &set, NULL);
int ret = sem_wait(&sem);
sigprocmask(SIG_UNBLOCK, &set, NULL);
if (ret == -1) {
if (errno == EINTR) {
// 重新处理信号等逻辑
} else {
perror("sem_wait error");
return 1;
}
}
// 临界区代码
sem_post(&sem);
sem_destroy(&sem);
return 0;
}
通过以上方式,确保在遇到EINTR
错误时,程序能够正确处理,继续正常执行信号量相关操作,维持系统的稳定性和健壮性。