<semaphore.h>
是一个 POSIX 标准定义的头文件,用于提供信号量(semaphore)的接口。信号量是用于线程或进程间同步的一种机制,可以控制访问共享资源的线程数目,广泛应用于多线程和多进程编程。
本文将详细介绍 <semaphore.h>
的数据类型、函数、以及示例。
1. 引入头文件
在使用信号量功能时,必须包含 <semaphore.h>
:
#include <semaphore.h>
2. 数据类型
sem_t
- 信号量的主要数据类型。
- 定义为一个结构体,用于存储信号量的当前值和相关信息。
- 可以用于进程间或线程间的同步。
3. 函数及用途
以下是 <semaphore.h>
中的主要函数:
(1) 初始化与销毁信号量
函数 | 描述 |
---|---|
int sem_init(sem_t *sem, int pshared, unsigned int value); | 初始化一个信号量。 |
int sem_destroy(sem_t *sem); | 销毁一个信号量。 |
sem_init
参数说明:sem
:指向信号量的指针。pshared
:- 如果为 0,信号量用于线程间同步。
- 如果为非 0,信号量用于进程间同步(需要位于共享内存中)。
value
:信号量的初始值(代表资源数目)。
- 返回值:成功返回
0
,失败返回-1
。
(2) 信号量操作
函数 | 描述 |
---|---|
int sem_wait(sem_t *sem); | 将信号量的值减 1。如果信号量值为 0,则阻塞直到信号量值大于 0。 |
int sem_trywait(sem_t *sem); | 尝试将信号量值减 1。如果信号量值为 0,不阻塞,返回错误。 |
int sem_post(sem_t *sem); | 将信号量的值加 1。如果有阻塞的线程,唤醒其中一个线程。 |
int sem_getvalue(sem_t *sem, int *sval); | 获取信号量的当前值。 |
4. 示例
(1) 线程间的同步
以下是一个简单的生产者-消费者问题示例,演示如何使用信号量控制线程同步:
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>#define BUFFER_SIZE 5int buffer[BUFFER_SIZE];
int count = 0;sem_t empty; // 表示空位的信号量
sem_t full; // 表示已占用位的信号量
pthread_mutex_t mutex; // 保护共享资源的互斥锁void *producer(void *arg) {for (int i = 0; i < 10; i++) {sem_wait(&empty); // 减少空位pthread_mutex_lock(&mutex); // 进入临界区buffer[count++] = i; // 放入数据printf("Produced: %d\n", i);pthread_mutex_unlock(&mutex); // 离开临界区sem_post(&full); // 增加已占用位sleep(1);}return NULL;
}void *consumer(void *arg) {for (int i = 0; i < 10; i++) {sem_wait(&full); // 减少已占用位pthread_mutex_lock(&mutex); // 进入临界区int item = buffer[--count]; // 取出数据printf("Consumed: %d\n", item);pthread_mutex_unlock(&mutex); // 离开临界区sem_post(&empty); // 增加空位sleep(2);}return NULL;
}int main() {pthread_t prod, cons;sem_init(&empty, 0, BUFFER_SIZE); // 初始化空位信号量sem_init(&full, 0, 0); // 初始化已占用信号量pthread_mutex_init(&mutex, NULL); // 初始化互斥锁pthread_create(&prod, NULL, producer, NULL);pthread_create(&cons, NULL, consumer, NULL);pthread_join(prod, NULL);pthread_join(cons, NULL);sem_destroy(&empty); // 销毁信号量sem_destroy(&full);pthread_mutex_destroy(&mutex); // 销毁互斥锁return 0;
}
输出示例
Produced: 0
Consumed: 0
Produced: 1
Produced: 2
Consumed: 1
Consumed: 2
...
(2) 进程间的同步
信号量也可以在进程间共享,但需要设置 sem_init
的 pshared
参数为非 0,并将信号量存储在共享内存中。
5. 注意事项
-
线程间 vs 进程间信号量:
pshared
为 0 时,信号量用于线程间同步。- 为非 0 时,需要将信号量置于共享内存区域,用于进程间同步。
-
阻塞与非阻塞操作:
sem_wait
会阻塞直到信号量值大于 0。sem_trywait
不会阻塞,而是立即返回成功或失败。
-
信号量的销毁:
- 在不再需要信号量时,必须调用
sem_destroy
释放资源。
- 在不再需要信号量时,必须调用
-
避免死锁:
- 正确管理信号量和互斥锁的顺序,以避免线程或进程死锁。
6. 总结
<semaphore.h>
提供了信号量相关的功能,适用于多线程或多进程场景下的同步问题。通过使用 sem_t
数据类型及相关函数,开发者可以有效地控制对共享资源的访问,确保线程安全。