Linux中信号量的相关操作
Linux系统中sem_init()函数
函数声明
c
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
函数参数详解
1. sem - 信号量指针
- 作用:指向要初始化的信号量对象
- 类型 :
sem_t * - 注意:必须指向有效的内存地址
c
// 正确示例
sem_t my_sem;
sem_init(&my_sem, 0, 1); // 使用栈上变量的地址
// 或者使用动态分配
sem_t *sem_ptr = malloc(sizeof(sem_t));
sem_init(sem_ptr, 0, 1);
2. pshared - 共享标志
- 作用:指定信号量的共享范围
- 取值 :
0:信号量在线程间共享(同一进程内)- 非0:信号量在进程间共享(需要放在共享内存中)
c
// 线程间共享
sem_t thread_sem;
sem_init(&thread_sem, 0, 1); // 线程间共享
// 进程间共享(需要特殊处理)
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
sem_t *create_process_semaphore() {
// 创建共享内存区域
sem_t *psem = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (psem != MAP_FAILED) {
sem_init(psem, 1, 1); // 进程间共享
}
return psem;
}
3. value - 初始值
- 作用:信号量的初始计数值
- 范围:必须是非负整数
- 含义 :
0:初始时无可用资源,所有等待者阻塞N:初始时有N个可用资源
c
// 二进制信号量(互斥锁)
sem_t binary_sem;
sem_init(&binary_sem, 0, 1); // 初始值为1,类似互斥锁
// 计数信号量(资源池)
#define POOL_SIZE 5
sem_t pool_sem;
sem_init(&pool_sem, 0, POOL_SIZE); // 初始有5个资源可用
函数返回值
- 成功 :返回
0 - 失败 :返回
-1,并设置相应的errno
c
#include <errno.h>
#include <stdio.h>
sem_t my_sem;
if (sem_init(&my_sem, 0, 1) == -1) {
perror("sem_init failed");
switch(errno) {
case EINVAL:
printf("参数无效\n");
break;
case ENOSYS:
printf("系统不支持信号量\n");
break;
default:
printf("未知错误\n");
}
exit(EXIT_FAILURE);
}
函数作用
sem_init() 用于初始化未命名的POSIX信号量,主要用于:
- 线程同步:协调多个线程的执行顺序
- 资源计数:限制对有限资源的并发访问
- 互斥保护:实现临界区的互斥访问
c
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#define THREAD_COUNT 3
sem_t counter_sem;
int shared_counter = 0;
void* thread_function(void* arg) {
int thread_id = *(int*)arg;
sem_wait(&counter_sem); // 获取信号量
{
// 临界区开始
int temp = shared_counter;
printf("线程%d读取值: %d\n", thread_id, temp);
sleep(1); // 模拟处理时间
shared_counter = temp + 1;
printf("线程%d写入值: %d\n", thread_id, shared_counter);
// 临界区结束
}
sem_post(&counter_sem); // 释放信号量
return NULL;
}
int main() {
pthread_t threads[THREAD_COUNT];
int thread_ids[THREAD_COUNT];
// 初始化二进制信号量(互斥信号量)
sem_init(&counter_sem, 0, 1);
// 创建线程
for (int i = 0; i < THREAD_COUNT; i++) {
thread_ids[i] = i;
pthread_create(&threads[i], NULL, thread_function, &thread_ids[i]);
}
// 等待所有线程完成
for (int i = 0; i < THREAD_COUNT; i++) {
pthread_join(threads[i], NULL);
}
printf("最终计数器值: %d\n", shared_counter);
sem_destroy(&counter_sem); // 销毁信号量
return 0;
}
使用注意事项
1. 内存管理
c
// 错误示例:使用未分配的内存
sem_t *bad_sem;
sem_init(bad_sem, 0, 1); // 段错误!bad_sem指向随机地址
// 正确示例
sem_t good_sem; // 栈上分配
sem_init(&good_sem, 0, 1);
// 或者堆上分配
sem_t *heap_sem = malloc(sizeof(sem_t));
sem_init(heap_sem, 0, 1);
// 记得最后要free(heap_sem);
2. 进程间共享的特殊要求
c
// 进程间共享信号量的正确用法
#include <sys/mman.h>
typedef struct {
sem_t proc_sem;
int shared_data;
} shared_memory_t;
shared_memory_t* create_shared_memory() {
// 创建共享内存区域
shared_memory_t *shm = mmap(NULL, sizeof(shared_memory_t),
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (shm == MAP_FAILED) {
return NULL;
}
// 在共享内存中初始化信号量
if (sem_init(&shm->proc_sem, 1, 1) == -1) {
munmap(shm, sizeof(shared_memory_t));
return NULL;
}
shm->shared_data = 0;
return shm;
}
3. 避免重复初始化
c
sem_t my_sem;
// 错误:重复初始化(未定义行为)
sem_init(&my_sem, 0, 1);
sem_init(&my_sem, 0, 1); // 错误!
// 正确:一次初始化,多次使用
void init_if_needed(sem_t *sem) {
static int initialized = 0;
if (!initialized) {
sem_init(sem, 0, 1);
initialized = 1;
}
}
易错点及解决方案
1. 忘记销毁信号量
c
// 错误:忘记调用sem_destroy
void problematic_function() {
sem_t sem;
sem_init(&sem, 0, 1);
// 使用信号量...
// 忘记调用sem_destroy(&sem); // 资源泄漏!
}
// 正确:使用RAII模式的思想
typedef struct {
sem_t sem;
int initialized;
} safe_sem_t;
void safe_sem_init(safe_sem_t *safe_sem, int pshared, unsigned int value) {
if (sem_init(&safe_sem->sem, pshared, value) == 0) {
safe_sem->initialized = 1;
}
}
void safe_sem_destroy(safe_sem_t *safe_sem) {
if (safe_sem->initialized) {
sem_destroy(&safe_sem->sem);
safe_sem->initialized = 0;
}
}
2. 错误的初始值设置
c
// 错误:初始值设置不当可能导致逻辑错误
sem_t sem;
sem_init(&sem, 0, 0); // 初始为0,所有等待者立即阻塞
// 如果这样使用可能造成死锁
sem_wait(&sem); // 立即阻塞,除非其他地方有sem_post
// 正确:根据需求合理设置初始值
#define MAX_CONNECTIONS 10
sem_t connection_pool;
sem_init(&connection_pool, 0, MAX_CONNECTIONS); // 资源池模式
完整使用示例
c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <errno.h>
#define BUFFER_SIZE 5
#define PRODUCER_COUNT 2
#define CONSUMER_COUNT 2
typedef struct {
int buffer[BUFFER_SIZE];
int in, out;
sem_t empty; // 空槽位信号量
sem_t full; // 满槽位信号量
sem_t mutex; // 互斥信号量
} buffer_t;
buffer_t shared_buffer;
void init_buffer(buffer_t *buf) {
buf->in = buf->out = 0;
// 初始化信号量
if (sem_init(&buf->empty, 0, BUFFER_SIZE) == -1) {
perror("sem_init empty failed");
exit(EXIT_FAILURE);
}
if (sem_init(&buf->full, 0, 0) == -1) {
perror("sem_init full failed");
sem_destroy(&buf->empty);
exit(EXIT_FAILURE);
}
if (sem_init(&buf->mutex, 0, 1) == -1) {
perror("sem_init mutex failed");
sem_destroy(&buf->empty);
sem_destroy(&buf->full);
exit(EXIT_FAILURE);
}
}
void destroy_buffer(buffer_t *buf) {
sem_destroy(&buf->empty);
sem_destroy(&buf->full);
sem_destroy(&buf->mutex);
}
void produce_item(buffer_t *buf, int item) {
sem_wait(&buf->empty); // 等待空槽位
sem_wait(&buf->mutex); // 进入临界区
// 生产项目
buf->buffer[buf->in] = item;
buf->in = (buf->in + 1) % BUFFER_SIZE;
printf("生产: %d\n", item);
sem_post(&buf->mutex); // 离开临界区
sem_post(&buf->full); // 增加满槽位计数
}
int consume_item(buffer_t *buf) {
sem_wait(&buf->full); // 等待满槽位
sem_wait(&buf->mutex); // 进入临界区
// 消费项目
int item = buf->buffer[buf->out];
buf->out = (buf->out + 1) % BUFFER_SIZE;
printf("消费: %d\n", item);
sem_post(&buf->mutex); // 离开临界区
sem_post(&buf->empty); // 增加空槽位计数
return item;
}
void* producer(void *arg) {
int id = *(int*)arg;
for (int i = 0; i < 5; i++) {
int item = id * 100 + i;
produce_item(&shared_buffer, item);
sleep(1);
}
return NULL;
}
void* consumer(void *arg) {
int id = *(int*)arg;
for (int i = 0; i < 5; i++) {
consume_item(&shared_buffer);
sleep(2);
}
return NULL;
}
int main() {
pthread_t producers[PRODUCER_COUNT];
pthread_t consumers[CONSUMER_COUNT];
int producer_ids[PRODUCER_COUNT];
int consumer_ids[CONSUMER_COUNT];
// 初始化缓冲区
init_buffer(&shared_buffer);
// 创建生产者线程
for (int i = 0; i < PRODUCER_COUNT; i++) {
producer_ids[i] = i;
pthread_create(&producers[i], NULL, producer, &producer_ids[i]);
}
// 创建消费者线程
for (int i = 0; i < CONSUMER_COUNT; i++) {
consumer_ids[i] = i;
pthread_create(&consumers[i], NULL, consumer, &consumer_ids[i]);
}
// 等待所有线程完成
for (int i = 0; i < PRODUCER_COUNT; i++) {
pthread_join(producers[i], NULL);
}
for (int i = 0; i < CONSUMER_COUNT; i++) {
pthread_join(consumers[i], NULL);
}
// 清理资源
destroy_buffer(&shared_buffer);
printf("程序执行完毕\n");
return 0;
}
总结
sem_init() 是POSIX信号量编程的基础函数,正确使用需要注意:
- 内存管理:确保信号量对象有有效的内存地址
- 共享模式:正确设置pshared参数
- 初始值:根据应用场景合理设置初始计数值
- 资源清理 :使用后务必调用
sem_destroy()释放资源 - 错误处理:检查返回值并适当处理错误情况
通过合理使用信号量,可以有效解决多线程/多进程环境下的同步和互斥问题。
Linux系统中sem_wait()函数
函数声明
c
#include <semaphore.h>
int sem_wait(sem_t *sem);
函数参数详解
sem - 信号量指针
- 作用:指向已初始化的信号量对象
- 类型 :
sem_t * - 要求:必须指向有效的、已初始化的信号量
c
#include <semaphore.h>
// 正确使用示例
sem_t my_sem;
void init_and_use_semaphore() {
// 1. 先初始化信号量
if (sem_init(&my_sem, 0, 1) == -1) {
perror("sem_init failed");
return;
}
// 2. 然后才能使用sem_wait
if (sem_wait(&my_sem) == -1) {
perror("sem_wait failed");
} else {
printf("成功获取信号量\n");
// 执行临界区代码...
sem_post(&my_sem); // 释放信号量
}
}
函数返回值
- 成功 :返回
0 - 失败 :返回
-1,并设置相应的errno
常见错误码:
EINVAL:参数sem不是有效的信号量EINTR:操作被信号中断
c
#include <stdio.h>
#include <errno.h>
#include <string.h>
void safe_sem_wait(sem_t *sem) {
int result;
do {
result = sem_wait(sem);
if (result == -1) {
if (errno == EINTR) {
// 被信号中断,继续等待
printf("sem_wait被信号中断,继续等待...\n");
continue;
} else {
// 其他错误
perror("sem_wait失败");
break;
}
}
} while (result == -1 && errno == EINTR);
if (result == 0) {
printf("成功获取信号量\n");
}
}
函数作用
sem_wait() 是POSIX信号量的核心操作函数,主要作用:
- 原子性减一操作:将信号量的值减1(如果值大于0)
- 阻塞等待:如果信号量值为0,则调用线程阻塞直到信号量值变为正数
- 同步控制:实现线程/进程间的同步和互斥
c
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
sem_t resource_sem;
int shared_resource = 0;
void* worker_thread(void* arg) {
int thread_id = *(int*)arg;
printf("线程%d: 尝试获取资源...\n", thread_id);
// sem_wait会阻塞直到信号量可用
sem_wait(&resource_sem);
printf("线程%d: 获得资源,处理中...\n", thread_id);
// 临界区开始
int temp = shared_resource;
sleep(1); // 模拟处理时间
shared_resource = temp + 1;
printf("线程%d: 更新共享资源为%d\n", thread_id, shared_resource);
// 临界区结束
sem_post(&resource_sem);
printf("线程%d: 释放资源\n", thread_id);
return NULL;
}
int main() {
pthread_t t1, t2;
int id1 = 1, id2 = 2;
// 初始化二进制信号量(互斥信号量)
sem_init(&resource_sem, 0, 1);
pthread_create(&t1, NULL, worker_thread, &id1);
pthread_create(&t2, NULL, worker_thread, &id2);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
printf("最终共享资源值: %d\n", shared_resource);
sem_destroy(&resource_sem);
return 0;
}
使用注意事项
1. 信号量必须先初始化
c
// 错误示例:使用未初始化的信号量
sem_t uninitialized_sem;
void wrong_usage() {
// 未定义行为!信号量未初始化
sem_wait(&uninitialized_sem); // 可能导致段错误或不可预测行为
}
// 正确示例
void correct_usage() {
sem_t sem;
sem_init(&sem, 0, 1); // 必须先初始化
sem_wait(&sem); // 然后才能使用
// ... 使用信号量
sem_post(&sem);
sem_destroy(&sem); // 最后销毁
}
2. 避免死锁
c
// 危险示例:可能导致死锁
sem_t sem1, sem2;
void* thread1(void* arg) {
sem_wait(&sem1);
sleep(1); // 模拟处理时间,增加死锁概率
sem_wait(&sem2); // 可能在这里死锁
// 临界区代码...
sem_post(&sem2);
sem_post(&sem1);
return NULL;
}
void* thread2(void* arg) {
sem_wait(&sem2); // 与thread1相反的获取顺序
sleep(1);
sem_wait(&sem1); // 死锁点
// 临界区代码...
sem_post(&sem1);
sem_post(&sem2);
return NULL;
}
// 解决方案:统一的获取顺序
void* safe_thread1(void* arg) {
sem_wait(&sem1);
sem_wait(&sem2); // 总是先获取sem1,再获取sem2
// 临界区代码...
sem_post(&sem2);
sem_post(&sem1);
return NULL;
}
void* safe_thread2(void* arg) {
sem_wait(&sem1); // 与thread1相同的获取顺序
sem_wait(&sem2);
// 临界区代码...
sem_post(&sem2);
sem_post(&sem1);
return NULL;
}
易错点及解决方案
1. 忘记释放信号量
c
// 错误示例:异常路径下忘记释放信号量
void risky_function(int value) {
sem_wait(&resource_sem);
if (value < 0) {
return; // 错误!这里直接返回,信号量没有释放!
}
if (some_error_condition) {
return; // 错误!另一个泄漏点!
}
// 正常处理...
sem_post(&resource_sem); // 只有正常路径会释放
}
// 正确解决方案1:使用goto统一清理
void safe_function1(int value) {
sem_wait(&resource_sem);
if (value < 0) {
goto cleanup; // 跳转到清理代码
}
if (some_error_condition) {
goto cleanup;
}
// 正常处理...
cleanup:
sem_post(&resource_sem);
}
// 正确解决方案2:使用do-while(0)模式
void safe_function2(int value) {
sem_wait(&resource_sem);
do {
if (value < 0) {
break;
}
if (some_error_condition) {
break;
}
// 正常处理...
} while(0);
sem_post(&resource_sem); // 无论哪种路径都会执行到这里
}
2. 信号中断处理
c
#include <signal.h>
volatile sig_atomic_t interrupted = 0;
void signal_handler(int sig) {
interrupted = 1;
}
// 正确处理信号中断的sem_wait
int robust_sem_wait(sem_t *sem) {
int result;
while ((result = sem_wait(sem)) == -1 && errno == EINTR) {
if (interrupted) {
printf("收到中断信号,退出等待\n");
return -1;
}
// 继续等待
printf("sem_wait被非致命信号中断,继续等待...\n");
}
return result;
}
void* interruptible_worker(void* arg) {
printf("线程开始工作...\n");
if (robust_sem_wait(&resource_sem) == 0) {
printf("成功获取信号量\n");
// 检查是否在等待过程中被中断
if (interrupted) {
printf("在等待过程中收到中断信号\n");
sem_post(&resource_sem);
return NULL;
}
// 正常处理...
sleep(2);
sem_post(&resource_sem);
printf("工作完成\n");
}
return NULL;
}
使用细节
1. 与sem_trywait()的区别
c
#include <stdio.h>
// sem_wait: 阻塞等待
// sem_trywait: 非阻塞尝试
void demonstrate_difference() {
sem_t sem;
sem_init(&sem, 0, 0); // 初始值为0,表示无可用资源
// 尝试非阻塞获取
if (sem_trywait(&sem) == -1) {
if (errno == EAGAIN) {
printf("sem_trywait: 信号量不可用,立即返回\n");
}
}
// 在另一个线程中post信号量
pthread_t tid;
pthread_create(&tid, NULL, void* arg -> void* {
sleep(1);
sem_post((sem_t*)arg);
return NULL;
}, &sem);
printf("sem_wait: 阻塞等待信号量...\n");
sem_wait(&sem); // 这里会阻塞直到另一个线程post
printf("sem_wait: 成功获取信号量\n");
pthread_join(tid, NULL);
sem_destroy(&sem);
}
2. 超时版本的sem_timedwait()
c
#include <time.h>
#include <stdio.h>
void demonstrate_timedwait() {
sem_t sem;
sem_init(&sem, 0, 0); // 初始为0,需要等待
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 2; // 设置2秒超时
printf("等待信号量,超时时间2秒...\n");
int result = sem_timedwait(&sem, &ts);
if (result == -1) {
if (errno == ETIMEDOUT) {
printf("sem_timedwait: 等待超时\n");
} else {
perror("sem_timedwait失败");
}
} else {
printf("sem_timedwait: 成功获取信号量\n");
sem_post(&sem);
}
sem_destroy(&sem);
}
完整生产者和消费者示例
c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <errno.h>
#define BUFFER_SIZE 5
#define PRODUCER_COUNT 3
#define CONSUMER_COUNT 2
#define ITEMS_PER_PRODUCER 4
typedef struct {
int buffer[BUFFER_SIZE];
int in, out, count;
sem_t mutex; // 保护缓冲区的互斥信号量
sem_t empty; // 空槽位计数
sem_t full; // 满槽位计数
} bounded_buffer_t;
bounded_buffer_t bb;
void buffer_init(bounded_buffer_t *buf) {
buf->in = buf->out = buf->count = 0;
sem_init(&buf->mutex, 0, 1);
sem_init(&buf->empty, 0, BUFFER_SIZE);
sem_init(&buf->full, 0, 0);
}
void buffer_destroy(bounded_buffer_t *buf) {
sem_destroy(&buf->mutex);
sem_destroy(&buf->empty);
sem_destroy(&buf->full);
}
void produce_item(bounded_buffer_t *buf, int item, int producer_id) {
// 等待空槽位
sem_wait(&buf->empty);
// 保护缓冲区访问
sem_wait(&buf->mutex);
// 生产项目
buf->buffer[buf->in] = item;
buf->in = (buf->in + 1) % BUFFER_SIZE;
buf->count++;
printf("生产者%d: 生产项目%d, 缓冲区中有%d个项目\n",
producer_id, item, buf->count);
sem_post(&buf->mutex);
sem_post(&buf->full); // 增加满槽位计数
}
int consume_item(bounded_buffer_t *buf, int consumer_id) {
// 等待有项目可消费
sem_wait(&buf->full);
// 保护缓冲区访问
sem_wait(&buf->mutex);
// 消费项目
int item = buf->buffer[buf->out];
buf->out = (buf->out + 1) % BUFFER_SIZE;
buf->count--;
printf("消费者%d: 消费项目%d, 缓冲区中有%d个项目\n",
consumer_id, item, buf->count);
sem_post(&buf->mutex);
sem_post(&buf->empty); // 增加空槽位计数
return item;
}
void* producer(void *arg) {
int producer_id = *(int*)arg;
for (int i = 0; i < ITEMS_PER_PRODUCER; i++) {
int item = producer_id * 100 + i;
produce_item(&bb, item, producer_id);
sleep(1); // 模拟生产时间
}
printf("生产者%d: 完成生产\n", producer_id);
return NULL;
}
void* consumer(void *arg) {
int consumer_id = *(int*)arg;
for (int i = 0; i < (ITEMS_PER_PRODUCER * PRODUCER_COUNT) / CONSUMER_COUNT; i++) {
int item = consume_item(&bb, consumer_id);
sleep(2); // 模拟消费时间
}
printf("消费者%d: 完成消费\n", consumer_id);
return NULL;
}
int main() {
pthread_t producers[PRODUCER_COUNT];
pthread_t consumers[CONSUMER_COUNT];
int producer_ids[PRODUCER_COUNT];
int consumer_ids[CONSUMER_COUNT];
// 初始化缓冲区
buffer_init(&bb);
// 创建生产者线程
for (int i = 0; i < PRODUCER_COUNT; i++) {
producer_ids[i] = i + 1;
pthread_create(&producers[i], NULL, producer, &producer_ids[i]);
}
// 创建消费者线程
for (int i = 0; i < CONSUMER_COUNT; i++) {
consumer_ids[i] = i + 1;
pthread_create(&consumers[i], NULL, consumer, &consumer_ids[i]);
}
// 等待所有线程完成
for (int i = 0; i < PRODUCER_COUNT; i++) {
pthread_join(producers[i], NULL);
}
for (int i = 0; i < CONSUMER_COUNT; i++) {
pthread_join(consumers[i], NULL);
}
// 清理资源
buffer_destroy(&bb);
printf("所有生产者和消费者线程完成\n");
return 0;
}
总结
sem_wait() 是POSIX信号量编程中最关键的函数之一,使用时需要注意:
- 初始化前置:确保信号量已正确初始化
- 错误处理:检查返回值,特别是EINTR处理
- 资源释放:确保所有路径都释放信号量,避免死锁
- 信号安全:在信号处理程序中避免使用
- 超时考虑:长时间阻塞时考虑使用sem_timedwait()
正确使用sem_wait()可以构建高效可靠的同步机制,是多线程编程的重要基础。
Linux系统中sem_post()函数
函数声明
c
#include <semaphore.h>
int sem_post(sem_t *sem);
函数参数详解
sem - 信号量指针
- 作用:指向已初始化的信号量对象
- 类型 :
sem_t * - 要求:必须指向有效的、已初始化的信号量
c
#include <semaphore.h>
#include <stdio.h>
// 正确使用示例
sem_t my_sem;
void init_and_use_semaphore() {
// 1. 先初始化信号量
if (sem_init(&my_sem, 0, 0) == -1) { // 初始值为0
perror("sem_init failed");
return;
}
// 2. 在某个条件满足时post信号量
printf("准备释放信号量...\n");
if (sem_post(&my_sem) == -1) {
perror("sem_post failed");
} else {
printf("信号量释放成功\n");
}
}
函数返回值
- 成功 :返回
0 - 失败 :返回
-1,并设置相应的errno
常见错误码:
EINVAL:参数sem不是有效的信号量EOVERFLOW:信号量值超过上限(理论可能,实际很少见)
c
#include <stdio.h>
#include <errno.h>
#include <string.h>
void safe_sem_post(sem_t *sem) {
if (sem_post(sem) == -1) {
switch(errno) {
case EINVAL:
printf("错误:无效的信号量指针\n");
break;
case EOVERFLOW:
printf("错误:信号量值超过上限\n");
break;
default:
printf("未知错误: %s\n", strerror(errno));
}
} else {
printf("信号量释放成功\n");
}
}
函数作用
sem_post() 是POSIX信号量的核心操作函数,主要作用:
- 原子性加一操作:将信号量的值加1
- 唤醒等待者:如果有线程/进程正在等待该信号量,则唤醒其中一个
- 同步通知:用于通知其他线程/进程资源可用或条件满足
c
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
sem_t data_ready;
int shared_data = 0;
void* producer_thread(void* arg) {
printf("生产者: 开始生产数据...\n");
sleep(2); // 模拟数据生产时间
// 生产数据
shared_data = 42;
printf("生产者: 数据生产完成,值=%d\n", shared_data);
// 通知消费者数据已就绪
sem_post(&data_ready);
printf("生产者: 已通知消费者\n");
return NULL;
}
void* consumer_thread(void* arg) {
printf("消费者: 等待数据...\n");
// 等待数据就绪
sem_wait(&data_ready);
printf("消费者: 收到通知,读取数据=%d\n", shared_data);
return NULL;
}
int main() {
pthread_t producer, consumer;
// 初始化信号量,初始值为0(表示数据未就绪)
sem_init(&data_ready, 0, 0);
pthread_create(&consumer, NULL, consumer_thread, NULL);
sleep(1); // 确保消费者先开始等待
pthread_create(&producer, NULL, producer_thread, NULL);
pthread_join(producer, NULL);
pthread_join(consumer, NULL);
sem_destroy(&data_ready);
return 0;
}
使用注意事项
1. 信号量必须先初始化
c
// 错误示例:使用未初始化的信号量
sem_t uninitialized_sem;
void wrong_usage() {
// 未定义行为!信号量未初始化
sem_post(&uninitialized_sem); // 可能导致段错误
}
// 正确示例
void correct_usage() {
sem_t sem;
sem_init(&sem, 0, 0); // 必须先初始化
// ... 一些操作
sem_post(&sem); // 然后才能使用
sem_destroy(&sem); // 最后销毁
}
2. 避免过度发布(overflow)
c
// 理论上可能的溢出问题(实际中很少见)
#define SEM_VALUE_MAX 2147483647 // 典型上限值
void demonstrate_overflow_risk() {
sem_t sem;
sem_init(&sem, 0, SEM_VALUE_MAX - 1);
sem_post(&sem); // 达到上限
printf("信号量值达到上限\n");
// 再次post可能导致溢出(行为未定义)
// sem_post(&sem); // 危险!
}
易错点及解决方案
1. 忘记调用sem_post(死锁)
c
// 错误示例:异常路径下忘记释放信号量
sem_t resource_sem;
void risky_critical_section(int condition) {
sem_wait(&resource_sem);
if (condition < 0) {
return; // 错误!这里直接返回,信号量没有释放!
}
if (some_complex_operation_fails()) {
return; // 错误!另一个泄漏点!
}
// 正常处理...
sem_post(&resource_sem); // 只有正常路径会释放
}
// 正确解决方案1:使用goto统一清理
void safe_critical_section1(int condition) {
sem_wait(&resource_sem);
if (condition < 0) {
goto cleanup;
}
if (some_complex_operation_fails()) {
goto cleanup;
}
// 正常处理...
cleanup:
sem_post(&resource_sem);
}
// 正确解决方案2:RAII包装器
typedef struct {
sem_t *sem;
} sem_guard_t;
void sem_guard_init(sem_guard_t *guard, sem_t *sem) {
sem_wait(sem);
guard->sem = sem;
}
void sem_guard_release(sem_guard_t *guard) {
if (guard->sem) {
sem_post(guard->sem);
guard->sem = NULL;
}
}
void safe_critical_section2(int condition) {
sem_guard_t guard;
sem_guard_init(&guard, &resource_sem);
// 无论发生什么,guard离开作用域时会自动释放
if (condition < 0) {
return;
}
if (some_complex_operation_fails()) {
return;
}
// 正常处理...
sem_guard_release(&guard);
}
2. 错误的发布顺序导致逻辑错误
c
// 错误示例:发布顺序不当
sem_t step1_done, step2_done;
void* incorrect_worker(void* arg) {
// 步骤1
printf("执行步骤1\n");
sem_post(&step1_done); // 过早通知
// 步骤2(可能耗时)
sleep(2);
printf("执行步骤2\n");
sem_post(&step2_done);
return NULL;
}
void* incorrect_coordinator(void* arg) {
sem_wait(&step1_done);
printf("协调器: 收到步骤1完成通知\n");
// 这里假设步骤2已经完成,但实际上可能还没有
sem_wait(&step2_done);
printf("协调器: 收到步骤2完成通知\n");
return NULL;
}
// 正确示例:在真正完成时发布
void* correct_worker(void* arg) {
// 步骤1
printf("执行步骤1\n");
// 不要立即post,等所有相关工作完成
// 步骤2
sleep(2);
printf("执行步骤2\n");
// 所有步骤完成后一次性通知
sem_post(&step1_done);
sem_post(&step2_done);
return NULL;
}
使用细节
1. 与sem_wait()的配对使用
c
#include <stdio.h>
#include <pthread.h>
// 展示正确的配对使用
sem_t counter_sem;
int counter = 0;
void* increment_thread(void* arg) {
for (int i = 0; i < 1000; i++) {
sem_wait(&counter_sem); // 获取访问权
counter++; // 临界区操作
sem_post(&counter_sem); // 释放访问权
}
return NULL;
}
void test_pairing() {
pthread_t t1, t2;
sem_init(&counter_sem, 0, 1); // 二进制信号量
pthread_create(&t1, NULL, increment_thread, NULL);
pthread_create(&t2, NULL, increment_thread, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
printf("最终计数器值: %d (应该是2000)\n", counter);
sem_destroy(&counter_sem);
}
2. 在条件变量模式中的使用
c
#include <pthread.h>
// 使用信号量实现简单的条件变量模式
sem_t condition_met;
int condition_flag = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void* waiter_thread(void* arg) {
printf("等待线程: 等待条件满足...\n");
sem_wait(&condition_met); // 等待条件
pthread_mutex_lock(&mutex);
printf("等待线程: 条件已满足,flag=%d\n", condition_flag);
pthread_mutex_unlock(&mutex);
return NULL;
}
void* setter_thread(void* arg) {
sleep(2); // 模拟准备工作
pthread_mutex_lock(&mutex);
condition_flag = 1;
printf("设置线程: 条件已设置\n");
pthread_mutex_unlock(&mutex);
// 通知所有等待者
sem_post(&condition_met);
printf("设置线程: 已通知等待者\n");
return NULL;
}
完整的生产者-消费者示例
c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <time.h>
#define BUFFER_SIZE 5
#define PRODUCER_COUNT 2
#define CONSUMER_COUNT 2
#define TOTAL_ITEMS 10
typedef struct {
int items[BUFFER_SIZE];
int count;
int in, out;
sem_t mutex; // 保护缓冲区的互斥信号量
sem_t empty_slots; // 空槽位计数
sem_t full_slots; // 满槽位计数
} thread_safe_buffer;
thread_safe_buffer buffer;
void buffer_init(thread_safe_buffer *buf) {
buf->count = 0;
buf->in = buf->out = 0;
// 初始化信号量
sem_init(&buf->mutex, 0, 1); // 互斥信号量
sem_init(&buf->empty_slots, 0, BUFFER_SIZE); // 初始有空槽位
sem_init(&buf->full_slots, 0, 0); // 初始无满槽位
}
void buffer_destroy(thread_safe_buffer *buf) {
sem_destroy(&buf->mutex);
sem_destroy(&buf->empty_slots);
sem_destroy(&buf->full_slots);
}
void buffer_produce(thread_safe_buffer *buf, int item, int producer_id) {
// 等待空槽位
sem_wait(&buf->empty_slots);
// 保护缓冲区操作
sem_wait(&buf->mutex);
// 生产项目
buf->items[buf->in] = item;
buf->in = (buf->in + 1) % BUFFER_SIZE;
buf->count++;
printf("生产者%d: 生产项目%d, 缓冲区大小=%d/%d\n",
producer_id, item, buf->count, BUFFER_SIZE);
sem_post(&buf->mutex);
// 通知消费者有新项目可用
sem_post(&buf->full_slots);
}
int buffer_consume(thread_safe_buffer *buf, int consumer_id) {
// 等待有项目可消费
sem_wait(&buf->full_slots);
// 保护缓冲区操作
sem_wait(&buf->mutex);
// 消费项目
int item = buf->items[buf->out];
buf->out = (buf->out + 1) % BUFFER_SIZE;
buf->count--;
printf("消费者%d: 消费项目%d, 缓冲区大小=%d/%d\n",
consumer_id, item, buf->count, BUFFER_SIZE);
sem_post(&buf->mutex);
// 通知生产者有空槽位
sem_post(&buf->empty_slots);
return item;
}
void* producer(void *arg) {
int producer_id = *(int*)arg;
int items_produced = 0;
while (items_produced < TOTAL_ITEMS / PRODUCER_COUNT) {
int item = rand() % 1000;
buffer_produce(&buffer, item, producer_id);
items_produced++;
// 随机延迟,模拟生产时间
usleep(rand() % 1000000);
}
printf("生产者%d: 完成,共生产%d个项目\n", producer_id, items_produced);
return NULL;
}
void* consumer(void *arg) {
int consumer_id = *(int*)arg;
int items_consumed = 0;
while (items_consumed < TOTAL_ITEMS / CONSUMER_COUNT) {
int item = buffer_consume(&buffer, consumer_id);
items_consumed++;
// 随机延迟,模拟消费时间
usleep(rand() % 1500000);
}
printf("消费者%d: 完成,共消费%d个项目\n", consumer_id, items_consumed);
return NULL;
}
int main() {
pthread_t producers[PRODUCER_COUNT];
pthread_t consumers[CONSUMER_COUNT];
int producer_ids[PRODUCER_COUNT];
int consumer_ids[CONSUMER_COUNT];
srand(time(NULL));
// 初始化缓冲区
buffer_init(&buffer);
printf("开始生产者-消费者演示...\n");
printf("缓冲区大小: %d, 生产者: %d, 消费者: %d, 总项目数: %d\n\n",
BUFFER_SIZE, PRODUCER_COUNT, CONSUMER_COUNT, TOTAL_ITEMS);
// 创建生产者线程
for (int i = 0; i < PRODUCER_COUNT; i++) {
producer_ids[i] = i + 1;
pthread_create(&producers[i], NULL, producer, &producer_ids[i]);
}
// 创建消费者线程
for (int i = 0; i < CONSUMER_COUNT; i++) {
consumer_ids[i] = i + 1;
pthread_create(&consumers[i], NULL, consumer, &consumer_ids[i]);
}
// 等待所有线程完成
for (int i = 0; i < PRODUCER_COUNT; i++) {
pthread_join(producers[i], NULL);
}
for (int i = 0; i < CONSUMER_COUNT; i++) {
pthread_join(consumers[i], NULL);
}
// 清理资源
buffer_destroy(&buffer);
printf("\n所有线程完成,程序结束\n");
return 0;
}
高级用法:信号量链
c
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#define STAGE_COUNT 3
sem_t stage_semaphores[STAGE_COUNT];
// 多阶段流水线处理
void* pipeline_stage(void* arg) {
int stage_id = *(int*)arg;
int next_stage = (stage_id + 1) % STAGE_COUNT;
printf("阶段%d: 启动\n", stage_id);
for (int i = 0; i < 3; i++) {
// 等待前一个阶段完成
sem_wait(&stage_semaphores[stage_id]);
printf("阶段%d: 处理项目%d\n", stage_id, i);
// 模拟处理时间
usleep(500000);
// 通知下一个阶段
sem_post(&stage_semaphores[next_stage]);
printf("阶段%d: 将项目%d传递给阶段%d\n", stage_id, i, next_stage);
}
printf("阶段%d: 完成\n", stage_id);
return NULL;
}
void demonstrate_pipeline() {
pthread_t stages[STAGE_COUNT];
int stage_ids[STAGE_COUNT];
// 初始化信号量链
for (int i = 0; i < STAGE_COUNT; i++) {
sem_init(&stage_semaphores[i], 0, 0);
}
// 启动第一个项目
sem_post(&stage_semaphores[0]);
// 创建流水线阶段线程
for (int i = 0; i < STAGE_COUNT; i++) {
stage_ids[i] = i;
pthread_create(&stages[i], NULL, pipeline_stage, &stage_ids[i]);
}
// 等待所有阶段完成
for (int i = 0; i < STAGE_COUNT; i++) {
pthread_join(stages[i], NULL);
}
// 清理
for (int i = 0; i < STAGE_COUNT; i++) {
sem_destroy(&stage_semaphores[i]);
}
}
总结
sem_post() 是POSIX信号量编程中的关键函数,使用时需要注意:
- 初始化前置:确保信号量已正确初始化
- 配对使用:与sem_wait()正确配对,避免死锁
- 异常安全:确保所有执行路径都调用sem_post()
- 发布时机:在适当的时机发布信号量,避免逻辑错误
- 资源清理:程序结束时销毁信号量
正确使用sem_post()可以实现复杂的同步模式,是多线程编程的重要基础。
Linux系统中sem_getvalue()函数
函数声明
c
#include <semaphore.h>
int sem_getvalue(sem_t *sem, int *sval);
函数参数详解
1. sem - 信号量指针
- 作用:指向已初始化的信号量对象
- 类型 :
sem_t * - 要求:必须指向有效的、已初始化的信号量
2. sval - 值存储指针
- 作用:指向存储信号量当前值的整数变量
- 类型 :
int * - 注意:必须指向有效的内存地址
函数返回值
- 成功 :返回
0 - 失败 :返回
-1,并设置相应的errno
常见错误码:
EINVAL:参数sem不是有效的信号量
函数作用
sem_getvalue() 用于获取信号量的当前值,主要用于:
- 监控和调试:查看信号量的状态
- 资源监控:了解可用资源数量
- 性能分析:监控系统负载情况
- 死锁检测:辅助诊断同步问题
基本使用示例
c
#include <stdio.h>
#include <semaphore.h>
#include <pthread.h>
#include <unistd.h>
sem_t my_sem;
void print_sem_value(const char* message) {
int value;
if (sem_getvalue(&my_sem, &value) == 0) {
printf("%s: 信号量值 = %d\n", message, value);
} else {
perror("sem_getvalue失败");
}
}
int main() {
// 初始化信号量
if (sem_init(&my_sem, 0, 3) == -1) {
perror("sem_init失败");
return 1;
}
print_sem_value("初始化后");
// 模拟一些操作
sem_wait(&my_sem);
print_sem_value("第一次wait后");
sem_wait(&my_sem);
print_sem_value("第二次wait后");
sem_post(&my_sem);
print_sem_value("一次post后");
sem_destroy(&my_sem);
return 0;
}
输出示例:
初始化后: 信号量值 = 3
第一次wait后: 信号量值 = 2
第二次wait后: 信号量值 = 1
一次post后: 信号量值 = 2
完整使用场景示例
1. 资源池监控系统
c
#include <stdio.h>
#include <semaphore.h>
#include <pthread.h>
#include <unistd.h>
#include <time.h>
#define POOL_SIZE 5
sem_t connection_pool;
int active_connections = 0;
// 监控线程:定期检查资源池状态
void* monitor_thread(void* arg) {
while (1) {
int available;
sem_getvalue(&connection_pool, &available);
time_t now = time(NULL);
struct tm* timeinfo = localtime(&now);
printf("[%02d:%02d:%02d] 监控报告: ",
timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec);
printf("可用连接: %d/%d, 活跃连接: %d\n",
available, POOL_SIZE, active_connections);
// 预警机制
if (available == 0) {
printf("⚠️ 警告: 连接池已空!\n");
}
if (available == POOL_SIZE) {
printf("✅ 连接池空闲\n");
}
sleep(2); // 每2秒监控一次
}
return NULL;
}
// 模拟客户端连接
void* client_thread(void* arg) {
int client_id = *(int*)arg;
printf("客户端 %d 尝试获取连接...\n", client_id);
sem_wait(&connection_pool);
active_connections++;
int available;
sem_getvalue(&connection_pool, &available);
printf("客户端 %d 获得连接,剩余可用: %d\n", client_id, available);
// 模拟工作
sleep(1 + (rand() % 3));
sem_post(&connection_pool);
active_connections--;
sem_getvalue(&connection_pool, &available);
printf("客户端 %d 释放连接,剩余可用: %d\n", client_id, available);
return NULL;
}
int main() {
pthread_t monitor, clients[10];
int client_ids[10];
srand(time(NULL));
// 初始化连接池信号量
sem_init(&connection_pool, 0, POOL_SIZE);
printf("=== 连接池监控系统启动 ===\n");
printf("连接池大小: %d\n\n", POOL_SIZE);
// 启动监控线程
pthread_create(&monitor, NULL, monitor_thread, NULL);
// 模拟多个客户端
for (int i = 0; i < 10; i++) {
client_ids[i] = i + 1;
pthread_create(&clients[i], NULL, client_thread, &client_ids[i]);
usleep(500000); // 间隔0.5秒
}
// 等待所有客户端完成
for (int i = 0; i < 10; i++) {
pthread_join(clients[i], NULL);
}
// 停止监控(在实际应用中需要更优雅的停止机制)
pthread_cancel(monitor);
pthread_join(monitor, NULL);
sem_destroy(&connection_pool);
printf("\n=== 系统关闭 ===\n");
return 0;
}
2. 生产者-消费者队列监控
c
#include <stdio.h>
#include <semaphore.h>
#include <pthread.h>
#include <unistd.h>
#define BUFFER_SIZE 10
typedef struct {
sem_t empty; // 空槽位信号量
sem_t full; // 满槽位信号量
int buffer[BUFFER_SIZE];
int in, out;
pthread_mutex_t mutex;
} bounded_buffer;
bounded_buffer bb;
void buffer_init(bounded_buffer* buf) {
sem_init(&buf->empty, 0, BUFFER_SIZE);
sem_init(&buf->full, 0, 0);
buf->in = buf->out = 0;
pthread_mutex_init(&buf->mutex, NULL);
}
void buffer_destroy(bounded_buffer* buf) {
sem_destroy(&buf->empty);
sem_destroy(&buf->full);
pthread_mutex_destroy(&buf->mutex);
}
// 获取缓冲区状态
void get_buffer_status(bounded_buffer* buf, int* empty_slots, int* full_slots) {
sem_getvalue(&buf->empty, empty_slots);
sem_getvalue(&buf->full, full_slots);
}
void* producer(void* arg) {
int producer_id = *(int*)arg;
int item = 0;
while (item < 20) { // 每个生产者生产20个物品
int empty, full;
// 生产前检查状态
get_buffer_status(&bb, &empty, &full);
printf("生产者%d: 准备生产,缓冲区状态 - 空位:%d, 满位:%d\n",
producer_id, empty, full);
sem_wait(&bb.empty);
pthread_mutex_lock(&bb.mutex);
// 生产物品
bb.buffer[bb.in] = ++item;
bb.in = (bb.in + 1) % BUFFER_SIZE;
pthread_mutex_unlock(&bb.mutex);
sem_post(&bb.full);
// 生产后检查状态
get_buffer_status(&bb, &empty, &full);
printf("生产者%d: 生产物品%d完成,缓冲区状态 - 空位:%d, 满位:%d\n",
producer_id, item, empty, full);
usleep(100000 + rand() % 200000); // 随机延迟
}
printf("生产者%d: 生产完成\n", producer_id);
return NULL;
}
void* consumer(void* arg) {
int consumer_id = *(int*)arg;
int consumed = 0;
while (consumed < 15) { // 每个消费者消费15个物品
int empty, full;
// 消费前检查状态
get_buffer_status(&bb, &empty, &full);
printf("消费者%d: 准备消费,缓冲区状态 - 空位:%d, 满位:%d\n",
consumer_id, empty, full);
sem_wait(&bb.full);
pthread_mutex_lock(&bb.mutex);
// 消费物品
int item = bb.buffer[bb.out];
bb.out = (bb.out + 1) % BUFFER_SIZE;
consumed++;
pthread_mutex_unlock(&bb.mutex);
sem_post(&bb.empty);
// 消费后检查状态
get_buffer_status(&bb, &empty, &full);
printf("消费者%d: 消费物品%d完成,缓冲区状态 - 空位:%d, 满位:%d\n",
consumer_id, item, empty, full);
usleep(150000 + rand() % 250000); // 随机延迟
}
printf("消费者%d: 消费完成\n", consumer_id);
return NULL;
}
// 监控线程
void* monitor_thread(void* arg) {
int iteration = 0;
while (iteration++ < 10) { // 监控10次
int empty, full;
get_buffer_status(&bb, &empty, &full);
printf("\n=== 监控报告 [%d] ===\n", iteration);
printf("空槽位: %d/%d\n", empty, BUFFER_SIZE);
printf("满槽位: %d/%d\n", full, BUFFER_SIZE);
printf("利用率: %.1f%%\n", (double)full / BUFFER_SIZE * 100);
// 性能建议
if (empty == BUFFER_SIZE) {
printf("建议: 增加消费者数量\n");
} else if (full == BUFFER_SIZE) {
printf("建议: 增加生产者数量\n");
} else if (empty > full) {
printf("状态: 生产速度 > 消费速度\n");
} else {
printf("状态: 消费速度 > 生产速度\n");
}
printf("==================\n\n");
sleep(2);
}
return NULL;
}
int main() {
pthread_t producers[2], consumers[3], monitor;
int producer_ids[] = {1, 2};
int consumer_ids[] = {1, 2, 3};
srand(time(NULL));
buffer_init(&bb);
printf("=== 生产者-消费者系统启动 ===\n");
printf("缓冲区大小: %d\n\n", BUFFER_SIZE);
// 启动监控线程
pthread_create(&monitor, NULL, monitor_thread, NULL);
// 启动生产者
for (int i = 0; i < 2; i++) {
pthread_create(&producers[i], NULL, producer, &producer_ids[i]);
}
// 启动消费者
for (int i = 0; i < 3; i++) {
pthread_create(&consumers[i], NULL, consumer, &consumer_ids[i]);
}
// 等待线程完成
for (int i = 0; i < 2; i++) {
pthread_join(producers[i], NULL);
}
for (int i = 0; i < 3; i++) {
pthread_join(consumers[i], NULL);
}
pthread_join(monitor, NULL);
buffer_destroy(&bb);
printf("=== 系统关闭 ===\n");
return 0;
}
使用注意事项
1. 竞态条件问题
c
// ❌ 错误示例:竞态条件
void unsafe_check(sem_t* sem) {
int value;
if (sem_getvalue(sem, &value) == 0) {
if (value > 0) {
// 这里其他线程可能已经修改了信号量值!
sem_wait(sem); // 可能阻塞!
}
}
}
// ✅ 正确示例:直接使用信号量操作
void safe_approach(sem_t* sem) {
// 直接尝试获取,让信号量机制处理同步
if (sem_trywait(sem) == 0) {
// 成功获取
} else {
// 无法获取(值为0或有等待者)
}
}
2. 错误处理最佳实践
c
#include <errno.h>
// 健壮的sem_getvalue封装
int robust_sem_getvalue(sem_t* sem, int* value) {
if (sem == NULL || value == NULL) {
errno = EINVAL;
return -1;
}
int result = sem_getvalue(sem, value);
if (result == -1) {
switch(errno) {
case EINVAL:
fprintf(stderr, "错误: 无效的信号量指针\n");
break;
default:
perror("sem_getvalue失败");
}
return -1;
}
return 0;
}
// 带重试的获取函数
int get_sem_value_with_retry(sem_t* sem, int* value, int max_retries) {
for (int i = 0; i < max_retries; i++) {
if (robust_sem_getvalue(sem, value) == 0) {
return 0; // 成功
}
if (errno != EINTR) { // 如果不是被信号中断,则退出重试
break;
}
usleep(100000); // 等待100ms后重试
}
return -1; // 重试多次后仍然失败
}
3. 性能监控工具类
c
#include <sys/time.h>
// 信号量性能监控器
typedef struct {
sem_t* sem;
char* name;
long monitor_interval_us;
int is_running;
pthread_t thread_id;
} sem_monitor_t;
void* monitor_function(void* arg) {
sem_monitor_t* monitor = (sem_monitor_t*)arg;
struct timeval start, end;
while (monitor->is_running) {
gettimeofday(&start, NULL);
int value;
if (sem_getvalue(monitor->sem, &value) == 0) {
gettimeofday(&end, NULL);
long response_time = (end.tv_sec - start.tv_sec) * 1000000 +
(end.tv_usec - start.tv_usec);
printf("监控[%s]: 值=%d, 响应时间=%ld微秒\n",
monitor->name, value, response_time);
}
usleep(monitor->monitor_interval_us);
}
return NULL;
}
// 启动监控
int start_monitoring(sem_monitor_t* monitor) {
monitor->is_running = 1;
return pthread_create(&monitor->thread_id, NULL, monitor_function, monitor);
}
// 停止监控
void stop_monitoring(sem_monitor_t* monitor) {
monitor->is_running = 0;
pthread_join(monitor->thread_id, NULL);
}
易错点和解决方案
1. 信号量未初始化
c
// ❌ 错误:使用未初始化的信号量
sem_t uninitialized_sem;
int value;
sem_getvalue(&uninitialized_sem, &value); // 未定义行为!
// ✅ 正确:确保信号量已初始化
sem_t my_sem;
if (sem_init(&my_sem, 0, 5) == 0) {
int value;
if (sem_getvalue(&my_sem, &value) == 0) {
printf("信号量值: %d\n", value);
}
}
2. 指针有效性检查
c
// ❌ 错误:无效指针
int* invalid_ptr = NULL;
sem_getvalue(&my_sem, invalid_ptr); // 段错误!
// ✅ 正确:检查指针有效性
int value;
if (sem_getvalue(&my_sem, &value) == 0) {
// 安全使用value
}
3. 多线程环境下的值过时问题
c
// 注意:获取的值可能立即过时
void print_current_status(sem_t* sem) {
int value1, value2;
sem_getvalue(sem, &value1);
// 在这里,其他线程可能已经修改了信号量值
sem_getvalue(sem, &value2);
printf("第一次获取: %d, 第二次获取: %d\n", value1, value2);
// 两个值可能不同!
}
实际应用案例
1. 负载均衡器
c
// 基于信号量值的简单负载均衡
typedef struct {
sem_t* sems[10]; // 10个工作队列的信号量
int queue_count;
} load_balancer_t;
int choose_best_queue(load_balancer_t* lb) {
int best_queue = -1;
int max_value = -1;
for (int i = 0; i < lb->queue_count; i++) {
int current_value;
if (sem_getvalue(lb->sems[i], ¤t_value) == 0) {
if (current_value > max_value) {
max_value = current_value;
best_queue = i;
}
}
}
return best_queue; // 返回最空闲的队列
}
2. 系统健康检查
c
// 系统健康监控
void system_health_check(sem_t* important_sems[], int sem_count) {
printf("=== 系统健康检查 ===\n");
for (int i = 0; i < sem_count; i++) {
int value;
if (sem_getvalue(important_sems[i], &value) == 0) {
const char* status = (value > 0) ? "正常" : "警告";
printf("信号量%d: 值=%d [%s]\n", i, value, status);
} else {
printf("信号量%d: 无法读取 [错误]\n", i);
}
}
printf("==================\n");
}
总结
sem_getvalue() 是一个有用的诊断工具,但需要注意:
- 不是同步原语:不能用于同步控制
- 值可能过时:在多线程环境中获取的值可能立即失效
- 主要用于监控:适合调试、监控和性能分析
- 需要错误处理:始终检查返回值
- 注意可移植性:不同系统可能有不同的实现细节
最佳实践:
- 用于系统监控和调试
- 结合其他诊断工具使用
- 在生产代码中谨慎使用
- 优先使用信号量操作(wait/post)进行同步