深入理解Semaphore:sem_init、sem_wait、sem_post详解及应用138


在并发编程的世界中,同步机制扮演着至关重要的角色。它们确保多个线程或进程能够安全地共享资源,避免出现数据竞争、死锁等问题。Semaphore(信号量)就是一种常用的同步原语,它允许程序控制对共享资源的访问次数。本文将深入探讨Linux系统中Semaphore的核心函数:`sem_init`、`sem_wait`(也称为`sem_p`)和`sem_post`(也称为`sem_v`),并结合实例讲解其用法和应用场景。

一、Semaphore的基本概念

Semaphore本质上是一个计数器,用于控制对共享资源的访问。它具有以下几个关键属性:
计数器值: 表示当前可用的资源数量。当计数器值为0时,表示资源已被全部占用;当计数器值大于0时,表示还有可用资源;计数器值可以是正数、0或负数(部分实现允许,但需要谨慎使用,通常建议保持非负)。
等待队列: 当资源被占用时,试图获取资源的进程或线程会被阻塞,并加入等待队列。当资源可用时,等待队列中的进程或线程会按一定顺序被唤醒。

Semaphore主要用于解决生产者-消费者问题、读者-写者问题等经典并发问题。与互斥锁(Mutex)不同,Semaphore可以允许多个线程同时访问共享资源(计数器值大于1),而互斥锁只能允许一个线程访问。

二、sem_init函数

sem_init函数用于初始化一个Semaphore。其函数原型如下:#include
int sem_init(sem_t *sem, int pshared, unsigned int value);

参数解释:
sem: 指向semaphore对象的指针。该对象需要事先声明为sem_t类型。
pshared: 指定Semaphore是进程内共享还是进程间共享。

0: 进程内共享,仅当前进程的线程可以访问。
1: 进程间共享,多个进程可以访问,需要使用共享内存等机制。


value: Semaphore的初始值,表示初始可用的资源数量。

返回值:成功返回0,失败返回-1,并设置errno。

三、sem_wait函数

sem_wait函数用于获取Semaphore资源。如果Semaphore计数器值大于0,则将其减1,并继续执行;如果计数器值为0,则当前线程会被阻塞,直到计数器值变为大于0。#include
int sem_wait(sem_t *sem);

参数解释:
sem: 指向Semaphore对象的指针。

返回值:成功返回0,失败返回-1,并设置errno(例如,中断)。

四、sem_post函数

sem_post函数用于释放Semaphore资源,将Semaphore计数器值加1。如果存在等待该Semaphore的线程,则唤醒其中一个线程。#include
int sem_post(sem_t *sem);

参数解释:
sem: 指向Semaphore对象的指针。

返回值:成功返回0,失败返回-1,并设置errno。

五、示例:生产者-消费者问题

下面是一个简单的生产者-消费者问题的示例,使用Semaphore进行同步:#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#define BUFFER_SIZE 5
int buffer[BUFFER_SIZE];
int buffer_count = 0;
sem_t empty;
sem_t full;
pthread_mutex_t mutex;
void *producer(void *arg) {
int i;
for (i = 0; i < 10; i++) {
sem_wait(&empty); // 等待空位
pthread_mutex_lock(&mutex); // 加锁
buffer[buffer_count++] = i;
printf("Producer produced: %d", i);
pthread_mutex_unlock(&mutex); // 解锁
sem_post(&full); // 信号满位
}
return NULL;
}
void *consumer(void *arg) {
int i;
for (i = 0; i < 10; i++) {
sem_wait(&full); // 等待满位
pthread_mutex_lock(&mutex); // 加锁
int item = buffer[--buffer_count];
printf("Consumer consumed: %d", item);
pthread_mutex_unlock(&mutex); // 解锁
sem_post(&empty); // 信号空位
}
return NULL;
}
int main() {
pthread_t p_thread, c_thread;
sem_init(&empty, 0, BUFFER_SIZE); // 初始化空位信号量
sem_init(&full, 0, 0); // 初始化满位信号量
pthread_mutex_init(&mutex, NULL); // 初始化互斥锁
pthread_create(&p_thread, NULL, producer, NULL);
pthread_create(&c_thread, NULL, consumer, NULL);
pthread_join(p_thread, NULL);
pthread_join(c_thread, NULL);
sem_destroy(&empty);
sem_destroy(&full);
pthread_mutex_destroy(&mutex);
return 0;
}

这段代码演示了如何使用sem_init、sem_wait和sem_post来协调生产者和消费者线程对共享缓冲区的访问,避免了数据竞争和死锁。

六、总结

sem_init、sem_wait和sem_post是操作Semaphore的关键函数,它们在并发编程中扮演着重要的角色。理解和熟练运用这些函数,对于编写高效、可靠的多线程或多进程程序至关重要。 需要注意的是,在使用Semaphore时,需要仔细考虑计数器的初始值以及各个线程对Semaphore的操作顺序,避免出现意外情况,例如死锁。

2025-04-19


上一篇:Linux内核同步机制:深入理解sem_t信号量

下一篇:神秘符号[sem sem yamhur nahxa]:解读古代未知文明的蛛丝马迹