面试题答案
一键面试1. 信号注册
在Linux中,可以使用sigaction
函数来注册信号处理函数。由于信号可能在任意时刻到达,为了避免竞态条件,建议使用一个专门的线程来处理信号。
#include <signal.h>
#include <pthread.h>
// 信号处理函数
void signal_handler(int signum) {
// 处理信号的逻辑
}
void* signal_handler_thread(void* arg) {
struct sigaction sa;
sa.sa_handler = signal_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
// 注册SIGTERM信号
sigaction(SIGTERM, &sa, NULL);
// 注册SIGUSR1信号
sigaction(SIGUSR1, &sa, NULL);
// 阻塞线程,等待信号
while (1) {
pause();
}
return NULL;
}
2. 线程间的协调
为了协调不同线程组之间的任务处理以及信号处理,可以使用以下几种机制:
- 条件变量(Condition Variable):用于线程间的同步。例如,当信号处理函数完成某些操作后,通过条件变量通知其他线程组进行相应的处理。
- 互斥锁(Mutex):保护共享资源,确保在同一时间只有一个线程能够访问共享资源。
pthread_mutex_t shared_resource_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t signal_cond = PTHREAD_COND_INITIALIZER;
// 线程组中的线程函数
void* thread_function(void* arg) {
// 加锁
pthread_mutex_lock(&shared_resource_mutex);
// 等待条件变量
pthread_cond_wait(&signal_cond, &shared_resource_mutex);
// 处理任务
// 解锁
pthread_mutex_unlock(&shared_resource_mutex);
return NULL;
}
3. 共享资源的保护
对于共享资源的保护,除了使用互斥锁之外,还可以考虑以下几点:
- 读写锁(Read-Write Lock):如果共享资源的读取操作远远多于写入操作,可以使用读写锁来提高并发性能。读操作可以并发进行,而写操作需要独占访问。
- 线程本地存储(Thread Local Storage, TLS):如果某些数据不需要在所有线程间共享,可以将其存储在线程本地,避免锁的竞争。
pthread_rwlock_t shared_resource_rwlock = PTHREAD_RWLOCK_INITIALIZER;
// 读取共享资源
void read_shared_resource() {
pthread_rwlock_rdlock(&shared_resource_rwlock);
// 读取操作
pthread_rwlock_unlock(&shared_resource_rwlock);
}
// 写入共享资源
void write_shared_resource() {
pthread_rwlock_wrlock(&shared_resource_rwlock);
// 写入操作
pthread_rwlock_unlock(&shared_resource_rwlock);
}
4. 性能瓶颈及优化
- 锁竞争:过多的锁操作会导致线程阻塞,降低系统的并发性能。优化方法包括减少锁的粒度,即只在必要的代码段加锁;使用更细粒度的锁,如读写锁;以及尽量减少共享资源的访问频率。
- 上下文切换:过多的线程切换会消耗系统资源,降低性能。可以通过合理设置线程数量,避免创建过多不必要的线程;采用线程池技术,复用线程,减少线程创建和销毁的开销。
- 信号处理开销:信号处理函数中的复杂操作可能会导致性能问题。可以将信号处理函数中的复杂操作简化,将实际的处理逻辑放到其他线程中执行,信号处理函数只负责触发相应的操作。
通过上述架构设计和优化策略,可以有效地处理高并发Linux C语言多线程应用中的信号处理和线程间协调问题,提高系统的性能和稳定性。