信号量sem_init,sem_wait,sem_post

本文介绍了信号量的基础概念,包括线程和进程间通信中的信号量使用。重点讲解了信号量的四种基本函数:sem_init、sem_wait、sem_post和sem_destroy的使用方法,并通过示例代码展示了信号量在多线程环境中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本篇文章是信号量的简单入门,主要学习关于信号量四个函数的使用。
文章综合整理了两篇文章:
http://blog.youkuaiyun.com/qyz_og/article/details/47189219
http://blog.youkuaiyun.com/ljianhui/article/details/10813469/
在此一并感谢~

一、什么是信号量

线程的信号量与进程间通信中使用的信号量的概念是一样,它是一种特殊的变量,它可以被增加或减少,但对其的关键访问被保证是原子操作。如果一个程序中有多个线程试图改变一个信号量的值,系统将保证所有的操作都将依次进行。

而只有0和1两种取值的信号量叫做二进制信号量,在这里将重点介绍。而信号量一般常用于保护一段代码,使其每次只被一个执行线程运行。我们可以使用二进制信号量来完成这个工作。

二、信号量的接口函数

信号量的函数都以sem_开头,线程中使用的基本信号量函数有4个,它们都声明在头文件semaphore.h中。

sem_init函数

该函数用于创建信号量,其原型如下:

int sem_init(sem_t *sem,int pshared,unsigned int value);
  • 1

该函数初始化由sem指向的信号对象,设置它的共享选项,并给它一个初始的整数值。
pshared控制信号量的类型,如果其值为0,就表示这个信号量是当前进程的局部信号量,否则信号量就可以在多个进程之间共享,value为sem的初始值。调用成功时返回0,失败返回-1.

sem_wait函数

该函数用于以原子操作的方式将信号量的值减1。原子操作就是,如果两个线程企图同时给一个信号量加1或减1,它们之间不会互相干扰。它的原型如下:

int sem_post(sem_t *sem);  
  • 1

sem指向的对象是由sem_init调用初始化的信号量。调用成功时返回0,失败返回-1.

sem_post函数

该函数用于以原子操作的方式将信号量的值加1。它的原型如下:

int sem_post(sem_t *sem);  
  • 1

与sem_wait一样,sem指向的对象是由sem_init调用初始化的信号量。调用成功时返回0,失败返回-1.

sem_destroy函数

该函数用于对用完的信号量的清理。它的原型如下:

int sem_destroy(sem_t *sem); 
  • 1

成功时返回0,失败时返回-1.

三、信号量的函数使用

代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>

  sem_t sem;

  void func1(void* arg)
  {
      sem_wait(&sem);
      int *running=arg;
      printf("thread running1\n");
      printf("%d\n",*running);
  }

  void func2(void* arg)
  {
      printf("pthread2 running\n");
      sem_post(&sem);
  }

  int main()
  {
      sem_init(&sem,0,0);
      pthread_t thread[2];
      int a=5;
      pthread_create(&(thread[0]),NULL,(void*)func1,(void*)&a);
      printf("main thread running\n");
      sleep(10);
      pthread_create(&(thread[1]),NULL,(void*)func2,(void*)&a);
      printf("main thread running2\n");
      pthread_join(thread[0],NULL);
      pthread_join(thread[1],NULL);
      sem_destroy(&sem);

      return 0;
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

首先创建信号量,第二个参数为0,表示这个信号量是当前进程的局部信号量,初始值为0.
然后使用pthread_create()函数创建两个线程,传入参数a。
线程1创建完成后,由于信号量初始化value=0,调用sem_wait会阻塞这个线程,信号量的值将减少1(此时<0),这个线程函数就会等待。
主线程sleep(10)后,线程2创建并执行调用,sem_post用于使信号量的值增加1,此时线程1可以执行。
使用pthread_join等待所创建的两个线程的结束。

编译:

 gcc testSem.c -o testSem -lpthread
 ./testSem 
  • 1
  • 2
  • 3

输出:

main thread running
main thread running2
pthread2 running
thread running1
5

--------------------- 本文来自 疯狂php 的优快云 博客 ,全文地址请点击:https://blog.youkuaiyun.com/u013457167/article/details/78318932?utm_source=copy

<think>嗯,用户这次提出了一个非常具体的Linux系统编程问题,关于信号量sem_wait无法正常执行的情况。从用户引用的资料来看,ta应该是在开发多线程程序时遇到了信号量同步异常,sem_wait没有按预期阻塞而是直接通过了。 用户可能正在调试一个关键的多线程程序,情绪上有些焦虑,因为信号量问题往往会导致难以复现的并发bug。ta引用的两段资料都聚焦在信号量初始化和wait/post的原子操作特性上,说明已经做过基础调研,但还没找到解决方案。 这个问题需要从几个维度分析: 首先是信号量初始化问题。用户引用中提到如果意外执行了sem_post会导致信号量值增加,这提示可能存在初始化逻辑错误。比如在初始化信号量之后、wait调用之前,是否有其他线程误调用了post操作?这会导致信号量初始值大于0。 其次是线程同步时序问题。用户引用的第二段材料详细说明了wait的原子特性:必须等待非零值才会减1。如果wait发现信号量值已经是正数,就会立即通过。结合用户描述的现象,很可能在调用wait之前,信号量值已经被意外增加。 另外还要考虑信号量作用域的问题。用户没有说明使用的是进程间信号量还是线程间信号量,这会影响问题排查方向。如果是进程间共享的信号量(PTHREAD_PROCESS_SHARED),还需要检查共享内存的同步机制。 解决方案应该分步骤验证:先用调试语句打印每次post/wait时的信号量值;然后检查所有可能修改信号量的代码路径;最后考虑使用sem_getvalue来实时监控。如果确认是时序问题,可能需要引入额外的同步机制,或者用sem_trywait配合循环来实现安全等待。 用户可能还需要知道:当信号量异常时,如何用strace跟踪系统调用?或者如何通过gdb检查信号量状态?这些高级调试技巧对解决此类问题很有帮助。</think>### Linux信号量`sem_wait`无法正常阻塞的问题分析及解决 #### 问题现象 当调用`sem_wait`时,预期线程应阻塞等待信号量值变为正数,但实际未阻塞而直接执行后续语句。此问题通常源于信号量值意外大于0。 --- #### 核心原因分析 1. **信号量初始化后意外递增** - 若在`sem_init()`后未调用`sem_wait`前,**其他线程或流程意外调用了`sem_post`**,会导致信号量值$ \text{sem\_value} > 0 $。 - 此时调用`sem_wait`会**立即执行减法操作并继续运行**(根据信号量原子操作特性)[^1][^2]。 2. **信号量未正确初始化** ```c sem_t sem; sem_init(&sem, 0, 1); // 初始值设为1 ``` - 若初始值$ \text{init\_value} \geq 1 $,首次`sem_wait`必然直接通过。 3. **竞争条件(Race Condition)** - 多线程中若存在**未受控的`sem_post`调用**,可能导致信号量值被意外增加。 --- #### 解决方法 1. **检查初始化值** 确认`sem_init`的第三个参数为预期初始值(通常应为0): ```c sem_init(&sem, 0, 0); // 初始值=0,确保首次sem_wait阻塞 ``` 2. **排查意外`sem_post`调用** - 检查代码中所有`sem_post`调用位置,确认是否在`sem_wait`前被触发。 - 添加日志追踪信号量值变化: ```c int val; sem_getvalue(&sem, &val); printf("Semaphore value: %d\n", val); // 调试输出 ``` 3. **使用`sem_trywait`验证状态** 在关键区域插入非阻塞检查: ```c if (sem_trywait(&sem) == 0) { // 成功获取信号量(值>0) } else { // 信号量为0,需等待 } ``` 4. **同步机制加固** - 对信号量操作区域**加锁**(如互斥锁),避免并发操作导致状态不一致: ```c pthread_mutex_lock(&mutex); sem_wait(&sem); // 在互斥锁保护下操作 pthread_mutex_unlock(&mutex); ``` 5. **替换为`sem_timedwait`** 设置超时检测阻塞逻辑: ```c struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += 2; // 设置2秒超时 if (sem_timedwait(&sem, &ts) == -1) { perror("sem_timedwait timeout"); } ``` --- #### 关键点总结 | 现象 | 根本原因 | 解决方向 | |---------------------|----------------------------|----------------------------| | `sem_wait`不阻塞 | 信号量值$ \text{sem\_value} > 0 $ | 检查初始化值、意外`sem_post`调用 | | 多线程下行为不稳定 | 竞争条件导致状态不一致 | 增加互斥锁保护操作区域 | > **信号量操作本质**:`sem_wait`仅在信号量值$ \leq 0 $时阻塞,否则立即减1继续执行[^2]。因此需确保其初始状态和时序符合设计预期。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值