你有没有遇到过这样的情况:家里几个人同时抢一台打印机,结果文件乱成一团?程序里的多个线程也一样,如果不加控制地访问共享资源,轻则数据出错,重则程序崩溃。这时候就得靠线程同步机制来协调,而信号量(Semaphore)就是其中一种实用的工具。
信号量是个啥?
你可以把信号量想象成公共厕所门口的钥匙牌。假设有3个隔间,门口就挂着3把钥匙。谁想上厕所,得先拿到钥匙才能进去。等出来时再把钥匙还回去。如果钥匙都被拿光了,后面的人就只能等着。信号量干的就是这个事——控制同时访问某个资源的线程数量。
它有两个核心操作:P操作(wait)和V操作(signal)。P操作是申请资源,信号量减1;V操作是释放资源,信号量加1。当信号量为0时,再有线程申请就会被阻塞,直到有其他线程释放资源。
代码里怎么用?
比如我们写个简单的例子,模拟5个线程争抢3个数据库连接:
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
sem_t db_sem;
void* access_db(void* arg) {
int thread_id = *(int*)arg;
printf("线程 %d 尝试获取数据库连接...\n", thread_id);
sem_wait(&db_sem); // P操作
printf("线程 %d 成功连接,正在操作...\n", thread_id);
sleep(2); // 模拟操作时间
printf("线程 %d 断开连接\n", thread_id);
sem_post(&db_sem); // V操作
return NULL;
}
int main() {
pthread_t threads[5];
int ids[5] = {1, 2, 3, 4, 5};
sem_init(&db_sem, 0, 3); // 初始化信号量为3
for (int i = 0; i < 5; i++) {
pthread_create(&threads[i], NULL, access_db, &ids[i]);
}
for (int i = 0; i < 5; i++) {
pthread_join(threads[i], NULL);
}
sem_destroy(&db_sem);
return 0;
}
运行后你会发现,前3个线程几乎同时进入,后两个会等一会儿才开始。这就是信号量在起作用——限制并发数,避免资源过载。
和互斥锁有啥不一样?
互斥锁就像一把钥匙只允许一个人进门,信号量则可以设置多把钥匙。也就是说,互斥锁本质是二值信号量(0或1),而普通信号量可以是任意正整数。当你需要控制“多少人能进”而不是“只能一人进”时,信号量更合适。
比如线程池、连接池这类场景,信号量用起来特别顺手。它不光能防冲突,还能做资源配额管理,比单纯的锁更灵活。