性能下降原因分析
- 信号量系统调用开销:在Linux中,信号量的PV操作通常通过系统调用实现。高并发场景下,频繁的系统调用会导致用户态与内核态之间的上下文切换,这会带来较大的开销,降低系统性能。
- 竞争激烈:大量线程或进程同时竞争信号量,容易导致信号量成为瓶颈。当一个线程获取不到信号量时,会进入睡眠状态,等待信号量释放。这种频繁的睡眠和唤醒操作会增加系统调度的负担,降低整体性能。
优化方案
方案一:减少系统调用次数 - 批量操作
- 优化思路:结合信号量PV操作原理,传统的PV操作每次只对信号量进行一次增减操作。可以设计一种机制,允许一次性对信号量进行多次P或V操作,从而减少系统调用次数。例如,如果一个线程需要多次获取信号量才能执行某个任务,可以将这些获取操作合并为一次系统调用。
- 代码实现:假设使用POSIX信号量,可通过自定义函数封装信号量操作。
#include <semaphore.h>
#include <stdio.h>
#include <pthread.h>
sem_t sem;
// 自定义批量P操作
void batch_P(sem_t *sem, int count) {
for (int i = 0; i < count; i++) {
sem_wait(sem);
}
}
// 自定义批量V操作
void batch_V(sem_t *sem, int count) {
for (int i = 0; i < count; i++) {
sem_post(sem);
}
}
void* thread_function(void* arg) {
batch_P(&sem, 3); // 一次性获取3个信号量
// 执行任务
printf("Thread is working...\n");
batch_V(&sem, 3); // 一次性释放3个信号量
return NULL;
}
int main() {
sem_init(&sem, 0, 5); // 初始化信号量值为5
pthread_t thread;
pthread_create(&thread, NULL, thread_function, NULL);
pthread_join(thread, NULL);
sem_destroy(&sem);
return 0;
}
方案二:采用读写锁优化读操作
- 优化思路:如果应用程序中存在大量的读操作和少量的写操作,基于信号量的PV操作会对读操作也进行严格的互斥控制,导致读操作之间不必要的等待。读写锁允许多个线程同时进行读操作,只有在写操作时才需要独占资源。在读操作时,使用读锁(共享锁),多个线程可以同时获取读锁进行读操作;在写操作时,使用写锁(排他锁),只有一个线程可以获取写锁进行写操作。这样可以提高系统在高并发读场景下的性能。
- 代码实现:假设使用POSIX读写锁。
#include <pthread.h>
#include <stdio.h>
pthread_rwlock_t rwlock;
int shared_data = 0;
void* read_thread(void* arg) {
pthread_rwlock_rdlock(&rwlock); // 获取读锁
printf("Reader thread reads data: %d\n", shared_data);
pthread_rwlock_unlock(&rwlock); // 释放读锁
return NULL;
}
void* write_thread(void* arg) {
pthread_rwlock_wrlock(&rwlock); // 获取写锁
shared_data++;
printf("Writer thread writes data: %d\n", shared_data);
pthread_rwlock_unlock(&rwlock); // 释放写锁
return NULL;
}
int main() {
pthread_rwlock_init(&rwlock, NULL);
pthread_t read_threads[3];
pthread_t write_thread;
for (int i = 0; i < 3; i++) {
pthread_create(&read_threads[i], NULL, read_thread, NULL);
}
pthread_create(&write_thread, NULL, write_thread, NULL);
for (int i = 0; i < 3; i++) {
pthread_join(read_threads[i], NULL);
}
pthread_join(write_thread, NULL);
pthread_rwlock_destroy(&rwlock);
return 0;
}