当前位置: 首页 > article >正文

基于进程信号量的多线程同步机制研究与实现

1 信号量

1.1 原理与概念

信号量机制本质是对于资源的预订操作,线程或者进程预订了之后,确保未来有一段时间,资源是属于我的。

对于预订资源,会有一个最小单位,资源都是以这个最小单位为整体被使用的。

信号量需要做到:

  1. 限制进来的进程数(保证每一个进来请求使用资源的进程都有一块资源)
  2. 合理的分配资源

这里,由于是信号量的前导,我们简单的把信号量理解为一个计数器(是由OS维护的)。

我们这里对于这个信号量的计数器的设计,提出几个问题?

1.计数器能不能简单的设计成一个整型变量?

不行,因为整型变量在经过进程创建之后,任意一个进程对他进行改变的时候,会发生写时拷贝,导致两个进程看到的不是同一个计数器,这样信号量的第一个目的,限制进入的进程数也就失效了。

2.count++和count--不是原子的。

3.申请sem和释放sem来保护临界资源,是规则。这个规则的由来?

 这个规则就是,程序员之间规定的规则,再使用多进程访问临界资源的时候,需要代码这样来保护临界资源。

4.所有的进程要访问临界资源,都需要先申请信号量,那么所有进程都需要看到同一个信号量,说明了信号量本身就是一个临界资源。那么我们需要利用临界资源去保护另一个临界资源,为了防止临界资源保护的嵌套,我们就需要保证信号量这个临界资源是安全的。

所以,信号量的申请(++)和信号量的释放(--)这两个操作都是原子的。

5.如果,信号量的初始值是1?

那么,这个信号量不就是一个二元信号量(不就是一把锁吗)

6.我们前面提到了信号量也需要合理的分配资源,那么由谁来做呢?

这里,也是由程序员,在代码部分来完成这项目标。

7.pv操作

我们把原子性的申请信号量称为p操作,原子性的释放信号量称为v操作。


 2 信号量的接口

2.1 初始化信号量:sem_init()

sem_init是Posix信号量操作中的一个函数,用于初始化一个定位在sem的匿名信号量。以下是对sem_init函数的详细解析:

一、函数原型

 

 二、参数说明

  • sem:指向sem_t类型的信号量对象的指针,该对象将被初始化。
  • pshared:指明信号量的共享属性。如果其值为0,则信号量将被进程内的线程共享;如果其值为非零,则信号量将在进程之间共享,此时信号量应定位在共享内存区域中。
  • value:指定信号量的初始值。这个值表示信号量可用的资源数目或信号灯的数目。

三、返回值

  • 成功时,sem_init函数返回0。
  • 失败时,sem_init函数返回-1,并将errno设置为合适的值以指示错误原因。

四、注意事项

  • 初始化一个已经初始化的信号量,其结果未定义。因此,在调用sem_init之前,应确保信号量对象未被初始化。
  • 当pshared为非零时,信号量必须存在于共享内存中,否则无法实现进程间共享。
  • 使用完信号量后,应调用sem_destroy函数来销毁它,以释放相关资源。


2.2 信号量等待:sem_wait()

sem_wait 是 POSIX 信号量(semaphore)API 中的一个函数,用于对信号量进行 (wait/down)操作。这个函数会阻塞调用它的线程,直到信号量的值大于零,然后它会将信号量的值减一并继续执行。如果信号量的值已经是零,则调用 sem_wait 的线程会被阻塞,直到信号量的值被其他线程通过 sem_post 增加。

一、函数原型

 

二、参数

  • sem:指向 sem_t 类型的信号量对象的指针。这个信号量对象应该是之前通过 sem_init 或 sem_open 初始化的。

三、返回值

  • 成功时,sem_wait 返回 0。
  • 失败时,sem_wait 返回 -1,并设置 errno 来指示错误的原因。可能的错误包括 EINVAL(无效的参数,即 sem 不是有效的信号量),EINTR(操作被信号中断),EDEADLK(死锁条件,如果信号量是通过 sem_init 初始化为线程间共享且调用线程已经拥有该信号量),以及 ENOSYS(如果系统不支持这个操作,尽管在现代 POSIX 系统中这不太可能)。

四、使用场景

sem_wait 通常用于保护临界区(critical section),确保同一时间只有一个线程(或进程,取决于信号量的共享属性)可以进入和执行临界区内的代码。这有助于避免多线程程序中的竞态条件(race conditions)和数据不一致问题。

五、注意事项

  • 在调用 sem_wait 之前,应确保信号量已经被正确初始化。
  • 如果信号量被设置为线程间共享(sem_init 的 pshared 参数非零),则所有操作该信号量的线程都应该在同一个进程内,或者信号量应该位于共享内存区域中,以便在不同进程间共享。
  • 在不再需要信号量时,应调用 sem_destroy 来销毁它(对于通过 sem_init 初始化的信号量)或 sem_close 和 sem_unlink(对于通过 sem_open 创建的命名信号量)。


2.3 信号量申请:sem_post

sem_post 是 POSIX 信号量(semaphore)API 中的一个函数,用于对信号量进行 V(signal/up)操作。这个函数会增加信号量的值,并可能唤醒一个或多个因为调用 sem_wait 而被阻塞的线程。

一、函数原型

 


二、参数

  • sem:指向 sem_t 类型的信号量对象的指针。这个信号量对象应该是之前通过 sem_init(对于匿名信号量)或 sem_open(对于命名信号量)初始化的。

三、返回值

  • 成功时,sem_post 返回 0。
  • 失败时,sem_post 返回 -1,并设置 errno 来指示错误的原因。可能的错误包括 EINVAL(无效的参数,即 sem 不是有效的信号量)。

四、使用场景

sem_post 通常与 sem_wait 一起使用,以实现线程间的同步。当一个线程完成了对临界区的访问后,它会调用 sem_post 来增加信号量的值,从而可能允许其他被阻塞的线程进入临界区。

五、注意事项

  • 在调用 sem_post 之前,应确保信号量已经被正确初始化。
  • 如果信号量被设置为线程间共享(sem_init 的 pshared 参数非零),则所有操作该信号量的线程都应该在同一个进程内,或者信号量应该位于共享内存区域中,以便在不同进程间共享。然而,需要注意的是,POSIX 信号量 API 并不直接支持跨进程的匿名信号量;跨进程共享通常是通过命名信号量(使用 sem_open)来实现的。
  • sem_post 的操作是原子的,即它不会被其他线程的 sem_wait 或 sem_trywait 调用中断。


2.4 信号量销毁:sem_destroy()

sem_destroy 是 POSIX 信号量(semaphore)API 中的一个函数,用于销毁一个由 sem_init 初始化的匿名信号量。这个函数会释放与信号量相关联的任何资源,并且只有在没有线程等待该信号量时才能成功调用。

一、函数原型

 


二、参数

  • sem:指向 sem_t 类型的信号量对象的指针。这个信号量对象应该是之前通过 sem_init 初始化的。

三、返回值

  • 成功时,sem_destroy 返回 0。
  • 失败时,sem_destroy 返回 -1,并设置 errno 来指示错误的原因。可能的错误包括 EBUSY(信号量当前正在被使用,即有线程正在等待它),以及 EINVAL(无效的参数,即 sem 不是有效的信号量)。

四、使用场景

sem_destroy 应该在信号量不再需要时被调用,以释放与其相关联的资源。这通常发生在程序结束或信号量完成其同步任务之后。

五、注意事项

  • 在调用 sem_destroy 之前,应确保没有线程在等待该信号量。如果有线程在等待,sem_destroy 将返回 -1 并设置 errno 为 EBUSY
  • sem_destroy 只能用于由 sem_init 初始化的匿名信号量。对于通过 sem_open 创建的命名信号量,应使用 sem_close 和 sem_unlink 来关闭和删除它们。
  • 一旦信号量被销毁,就不能再对它进行任何操作(如 sem_waitsem_post 等),否则会导致未定义的行为。

http://www.kler.cn/a/456618.html

相关文章:

  • HTML5 开关(Toggle Switch)详细讲解
  • RabbitMQ中的异步Confirm模式:提升消息可靠性的利器
  • STM32 高级 WIFi案例1:测试AT指令
  • c#中的事件和委托
  • 循环神经网络(RNN)入门指南:从原理到实践
  • Flutter中添加全局防护水印的实现
  • Vue 3 与 Tauri 集成开发跨端APP
  • NLP 中文拼写检测纠正论文 Automatic-Corpus-Generation
  • ArcGIS Pro地形图四至角图经纬度标注与格网标注
  • 螺杆支撑座在运用中会出现哪些问题?
  • 微信流量主挑战:用户破16!新增文档转换(新纪元3)
  • CBSD管理QEMU仿真虚拟机
  • 穷举vs暴搜vs深搜vs回溯vs剪枝系列一>组合
  • 高效搭建Nacos:实现微服务的服务注册与配置中心
  • 字符串函数和结构题内存对齐
  • 虚幻引擎结构之ULevel
  • 小程序配置文件 —— 14 全局配置 - tabbar配置
  • 深度学习笔记(5)——目标检测和图像分割
  • 前端进阶之副作用的分析和控制
  • 微服务-1 认识微服务
  • 用命令行重启资源管理器(记录win解决找不到资源管理器问题)
  • 【 Git 设置代理】
  • upload-labs关卡记录8
  • Java基于SpringBoot的社区团购系统的设计与实现,附源码
  • Clickhouse使用基础
  • 【可靠有效】springboot使用netty搭建TCP服务器