Linux之线程-信号量sem_*

2023-05-16

1. 概念

信号量可理解为进化版的互斥锁/量,允许多个线程访问共享资源。由于互斥锁的力度比较大,如果希望在多个线程间对某一对象的部分数据进行共享,使用互斥锁是没有办法实现的,只能将整个数据对象锁住。这样虽然达到了多线程操作共享数据时保证数据正确的目的,却无形中导致线程的并发性下降。线程从并行执行变成了串行执行,与直接使用单进程无异。信号量,是相对折中的一种处理方式,既能保证同步,数据不混乱,又能提高线程并发。

2.主要应用函数

  • sem_init函数
  • sem_destroy函数
  • sem_wait函数
  • sem_trywait函数
  • sem_timedwait函数
  • sem_post函数

以上6个函数的返回值都是:成功返回0,失败返回-1,同时设置errno。(注意它们没有pthread前缀);

sem_t类型,本质仍然是结构体,但应用期间可简单看做为整数,忽略实现细节(类似于使用文件描述符);

sem_t sem;规定信号量sem不能<0,头文件#include<semaphore.h>。

3.信号量基本操作

sem_wait:  (1)信号量大于0,则信号量--自减,类比pthread_mutex_lock;(2)信号量等于0,造成线程阻塞。

      |

   对应

      |

sem_post: 信号量++自加,同时唤醒阻塞在信号量上的线程,类比pthread_mutex_unlock。

但,由于sem_t的实现对用户隐藏,所以所谓的++、--操作只能通过函数实现,而不能直接++、--符号运算。信号量的初值,决定了占用信号量的线程的个数。

4.具体函数

(1)sem_init函数

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

作用:初始化一个信号量

参数:sem, 信号量;pshared,取0用于线程间,取非0(一般为1)用于进程间;value, 指定信号量的初值。

(2)sem_destroy函数

int sem_destroy(sem_t *sem);

作用:销毁一个信号量

(3)sem_wait函数

int sem_wait(sem_t *sem);

       int sem_trywait(sem_t *sem);

       int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

作用:给信号量加锁、尝试加锁,--。申请信号量,申请成功,value--。

sem_timedwait()与sem_wait()相同,不同之处在于abs_timeout指定了在无法立即执行减量操作时调用应阻塞的时间限制。 abs_timeout参数指向一个结构,该结构指定自1970年1月1日00:00:00 +0000(UTC)以来的绝对超时(以秒和纳秒为单位)。
        此结构定义如下:
            struct timespec {
                time_t tv_sec; / *秒* /
                long   tv_nsec; / *纳秒[0 .. 999999999] * /
            };
        如果超时已在调用时到期,并且信号无法立即锁定,则sem_timedwait()会失败,并显示超时错误(将errno设置为ETIMEDOUT)。
        如果可以立即执行该操作,则无论abs_timeout的值如何,sem_timedwait()都不会因超时错误而失败。 此外,在这种情况下不检查abs_timeout的有效性。

(4)sem_post函数

int sem_post(sem_t *sem);

作用:给信号量解锁,释放信号量value++。

5.实例--生产者与消费者模型

/*生产者与消费者模型,采用信号量'维持'正常有序运转*/

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

sem_t blank, xfull;
#define _SEM_CNT_ 5    //指定信号量的初值,定义一个全局变量表示

int queue[_SEM_CNT_];  //模拟生产者存放产品的“仓库”,生产者每生产1个产品,仓库空位blank减去1,消费者每从仓库消费1个产品,仓库空位置腾出来1个blank+1。
int beginnum = 100;    //产品编号从100开始计数

void *thr_producter(void *arg)
{
    int i=0;
    while (1)
    {
        sem_wait(&blank);                     //申请仓库中1个空位置资源,blank--
        printf("-------%s----self---%lu---num---%d\n",__FUNCTION__,pthread_self(),beginnum);
        queue[(i++)%_SEM_CNT_] = beginnum++;  //生产1个产品
        sem_post(&xfull);                     //xfull++,仓库占用位置+1
        sleep(rand()%3);
    }
    return NULL;
}

void *thr_customer(void *arg)
{
    int i=0;
    int num=0;
    while (1)
    {
        sem_wait(&xfull);                //消费者等待消费,仓库仓库占用位置腾出1个,xfull--
        num = queue[(i++)%_SEM_CNT_];    //消费
        printf("-------%s----self---%lu---num---%d\n",__FUNCTION__,pthread_self(),num);
        sem_post(&blank);                //消费完腾出一个空位置blank++给生产者继续生产
        sleep(rand()%3);
    }
    
    return NULL;
}

int main()
{
    sem_init(&blank,0,_SEM_CNT_);  //初始化信号量,生产者
    sem_init(&xfull,0,0);  //消费者一开始的初始化默认没有产品,也就是信号量初值value为0

    //创建线程
    pthread_t tid[2];
    pthread_create(&tid[0],NULL,thr_producter,NULL);
    pthread_create(&tid[1],NULL,thr_customer,NULL);

    //线程分离
    pthread_join(&tid[0],NULL);
    pthread_join(&tid[1],NULL);

    //销毁信号量
    sem_destroy(&blank);
    sem_destroy(&xfull);
    return 0;
}

输出:

~$ gcc sem_product.c -lpthread

~$ ./a.out
-------thr_producter----self---140089735280384---num---100
-------thr_customer----self---140089726887680---num---100
-------thr_producter----self---140089735280384---num---101
-------thr_customer----self---140089726887680---num---101
-------thr_producter----self---140089735280384---num---102
-------thr_customer----self---140089726887680---num---102
-------thr_producter----self---140089735280384---num---103
-------thr_customer----self---140089726887680---num---103
-------thr_producter----self---140089735280384---num---104
-------thr_customer----self---140089726887680---num---104
-------thr_producter----self---140089735280384---num---105
-------thr_customer----self---140089726887680---num---105

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux之线程-信号量sem_* 的相关文章

  • 如何从 C 文件更改终端中的目录

    如何从 C 程序更改将在终端上生效的目录 实际上不要告诉 system 函数或 chdir 函数 这些仅适用于 C 中的进程或子 shell 假设我正在从 bash shell 执行一个 C 程序 其进程 ID 为 10223 那么 我可以
  • 从 gitlab docker runner 启动声纳扫描仪

    我有一个 CI 工作流程 集成了 linting 作业和代码质量作业 我的 Linting 工作是一个 docker runner 从应用程序代码启动我的 eslint 脚本 然后我的代码质量工作应该启动声纳扫描仪泊坞窗实例 检查我的代码并
  • Linux:通过网络进行屏幕桌面视频捕获和 VNC 帧速率

    抱歉 文字墙很长 TL DR VNC 连接的帧速率是多少 以帧 秒为单位 或者更确切地说 由谁决定 客户端还是服务器 对于桌面屏幕捕获的任何其他建议 但 正确的时间编码 具有不抖动的帧速率 具有稳定的周期 并有可能将其作为未压缩 或无损 图
  • 代码::块 - 警告:GDB:无法设置控制终端:不允许操作

    我已经通过官方存储库在 Ubuntu 14 04 中安装了 Code Blocks 13 12 当我编译时 一切正常 但是当我调试时 shell 中会显示以下消息 警告 GDB 无法设置控制终端 操作不正确 允许的 程序执行到断点 但当我执
  • 当存在点和下划线时,使用 sed 搜索并替换

    我该如何更换foo with foo sed 只需运行 sed s foo foo g file php 不起作用 逃离 sed s foo foo g file php Example cat test txt foo bar sed s
  • 在Linux伪终端中执行从一个终端发送到另一个终端的字符串

    假设我有一个终端 其中 tty 的输出是 dev pts 2 我想从另一个终端向第一个终端发送命令并执行它 使用 echo ls gt dev pts 2 仅在第一个终端中打印 ls 有没有办法执行字符串 不 终端不执行命令 它们只是数据的
  • Nasm 打印到下一行

    我用 nasm Assembly 编写了以下程序 section text global start start Input variables mov edx inLen mov ecx inMsg mov ebx 1 mov eax 4
  • 使用 sed 将 old-link-url 替换为 new-link-url

    我正在 bash 中编写一个脚本 将 old link url 替换为 new link url 我的问题是 sed 由于斜杠而无法替换 url 如果我只输入一些文字就可以了 my code sed e s old link new lin
  • ARM 系统调用的接口是什么?它在 Linux 内核中的何处定义?

    我读过有关 Linux 中的系统调用的内容 并且到处都给出了有关 x86 架构的描述 0x80中断和SYSENTER 但我无法追踪 ARM 架构中系统调用的文件和进程 任何人都可以帮忙吗 我知道的几个相关文件是 arch arm kerne
  • 退出 bash 脚本但保持进程运行

    我正在运行服务器 需要使用参数执行以下命令 这些脚本目前工作得很好 但问题是当我运行脚本时我无法返回到控制台 它在控制台中保持运行 如果我强行停止它 那么该过程也会停止 我想继续运行该进程并返回到控制台 bin sh php home st
  • gnome-terminal 新选项卡,使用别名作为要执行的命令

    我已经创建了一个别名 bashrc文件如下 alias myproject cd Desktop myproject 当我重新启动终端时保存文件后 输入myproject带我到项目目录 但是当我尝试使用别名作为新的命令参数时gnome te
  • 如何从“git log”中查看 Git 中的特定版本?

    My git log显示为 enter code here git trial git log commit 4c5bc66ae50780cf8dcaf032da98422aea6e2cf7 Author king lt email pro
  • 如何在两个不同帐户之间设置无密码身份验证

    我们可以在两台机器的两种不同用途之间设置无密码身份验证吗 例如 计算机A有用户A 计算机B有用户B 我们可以设置密码 ssh 以便计算机 A 上的用户 A 使用其用户帐户 A 登录计算机 B 谢谢你 如果我理解你的问题 你能设置一下吗ssh
  • 编写多个mysql脚本

    是否可以在复合脚本中包含其他 mysql 脚本 理想情况下 我不想为包含的脚本创建存储过程 对于较大的项目 我想分层维护几个较小的脚本 然后根据需要组合它们 但现在 我很乐意学习如何包含其他脚本 source是一个内置命令 您可以在 MyS
  • 远程linux服务器到远程linux服务器大型稀疏文件复制 - 如何?

    我有两台 CentOS 5 4 服务器 每台服务器上都安装了 VMware Server 假设我始终对 vmware 虚拟机使用稀疏文件 将虚拟机文件从一台服务器复制到另一台服务器的最可靠 最快速的方法是什么 虚拟机的文件复制起来很痛苦 因
  • 使用脚本自动输入 SSH 密码

    我需要创建一个自动向 OpenSSH 输入密码的脚本ssh client 假设我需要通过 SSH 进入myname somehost用密码a1234b 我已经尝试过 bin myssh sh ssh myname somehost a123
  • 使用命令行将 MediaWiki 维基文本格式转换为 HTML

    我倾向于编写大量文档 因此 MediaWiki 格式对我来说很容易理解 而且比编写传统 HTML 节省了我很多时间 然而 我也写了一篇博客 发现一直从键盘切换到鼠标来输入正确的 HTML 标签会增加很多时间 我希望能够使用 Mediawik
  • linux下如何获取昨天和前天?

    我想在变量中获取 sysdate 1 和 sysdate 2 并回显它 我正在使用下面的查询 它将今天的日期作为输出 bin bash tm date Y d m echo tm 如何获取昨天和前天的日期 这是另一种方法 对于昨天来说 da
  • Bash 脚本 - 迭代 find 的输出

    我有一个 bash 脚本 其中需要迭代 find 命令输出的每一行 但似乎我正在迭代 find 命令中的每个单词 以空格分隔 到目前为止我的脚本看起来像这样 folders find maxdepth 1 type d for i in f
  • 如何从 Linux 的 shell 中删除所有以 ._ 开头的文件?

    确实如标题所示 我已将许多文件从 Mac 复制到 Raspberry Pi 这导致了许多以前缀开头的多余文件 我想删除以以下开头的文件夹中的每个文件 我该怎么做 尝试类似的方法 cd path to directory rm rf 或者 如

随机推荐

  • Linux 磁盘坏块修复处理(错误:read error: Input/output error)

    当磁盘出现坏块时 xff0c 你对所关联的文件进行读取时 xff0c 一般会出现 read error Input output error 这样的错误 反过来讲 xff0c 当你看到 read error Input output err
  • docker给运行中的容器添加端口映射

    问题描述 docker上面运行容器 xff0c run p 的时候只映射了一个端口 xff0c 后面对该nginx做扩展 xff0c 需要开放其他端口 当然重新再启一个容器在docker启动参数里多配置几个 p端口映射是能解决的 xff0c
  • Git常用命令符

    1 强制推送 xff08 慎用 xff0c 除非你认为其他冲突等可以丢弃 或者不是很重要 xff09 git push force 2 创建文件等小命令 touch a 创建一个a文件 echo 1234 gt gt a 把1234这个内容
  • 全网最全的 LeetCode 国人大神刷题指南,全部 Go 语言实现

    大家好 xff0c 我是欧盆索思 xff08 opensource xff09 xff0c 每天为你带来优秀的开源项目 xff01 说到 LeetCode xff0c 作为一个程序员来说 xff0c 应该不陌生 xff0c 近几年参加面试都
  • ROS实现串口通信

    虚拟串口的搭建 参考Linux下添加虚拟串口 xff0c 接收和发送数据 com py文件代码如下 xff1a span class token comment usr bin env python span span class toke
  • 基于Linux的UART驱动框架源码分析笔记

    文章目录 前言一 I MX6ULL串口接收和发送方式1 非DMA方式1 1 接收方式1 2 发送方式 2 DMA方式2 1 接收方式2 2 发送方式 二 UART驱动注册1 uart register driver 函数解析2 serial
  • cmake install 命令

    install指令用于定义安装规则 xff0c 安装的内容可以包括目标二进制 动态库 静态库以及文件 目录 脚本等 需要引入一个新的cmake指令和一个非常有用的变量 cmake install prefix 法一 xff1a cmake
  • 基于全景相机的视觉里程计算法研究

    一 视觉里程计 视觉里程计技术首先建立相机的成像模型 xff0c 接着通过标定算法计算相机参数 xff0c 最后建立相邻图像的关联并估计相机运动轨迹 1 1相机在空间中运动的描述 描述相机在三维空间中的运动状态 xff0c 即求解相机在空间
  • 多旋翼无人机组成(小白上路)

    1 无人机组成 1 1 机架 四旋翼最常见的两种机身布局如下图 xff1a 机架指无人机的承载平台 xff0c 通常用轴距衡量机架的大小 xff0c 轴距是指对角线两个螺旋桨的距离 xff0c 一般以mm为单位 xff0c 如F330表示轴
  • freertos创建任务后进入prvStartFirstTask发生HardFault_Handler中断

    在stm32f103zet6环境中移植成功freertos之后 xff0c 创建第一个任务之后 xff0c 会进入硬件中断 xff0c 经过排查发现死在了prvStartFirstTask 排查原因 xff1a 发现是启动文件startup
  • 解决无法对docker容器进行端口映射的问题

    初学docker的时候 xff0c 不知道为啥 xff0c 按着教程里打的代码 xff0c 最后却出现了映射失败的情况 即 xff1a 在docker内部设置的映射端口 xff0c 外部却没有办法访问 想了想 xff0c 不外乎两个原因 x
  • K8s手工创建kubeconfig

    我们通过 kubectl 命令行连接 k8s apiserver 时需要依赖 kubeconfig 文件 kubeconfig 文件通常包含了 context xff08 上下文 xff09 列表 xff0c 每个 context 又会引用
  • grep命令总结

    grep命令总结 1 关于 nbsp ps ef grep php grep v grep wc l grep v grep 代表在查询的最终结果中去掉grep命令本身 wc l 标示统计查询到的结果数量 grep常用命令 1 grep n
  • Ubuntu 16.04安装realsense D435i SDK以及realsense-ros

    先直接上一个报错信息 xff0c 折腾了半天才解决 在使用catkin make编译realsense ros时 xff0c 报错 traversing 4 packages in topological order realsense c
  • 关于视觉SLAM的一些常识(纯小白学习笔记)

    本文只是小白对于视觉slam的一个非常泛的介绍 xff0c 对于视觉slam中的数学运算均没有提及 xff0c 适合于对没有接触过视觉slam的新人进行一个简单的科普 作者即小白 xff0c 文章如有错误 xff0c 非常非常非常欢迎指正
  • 使用CubeMX快速搭建FREERTOS

    如何使用STM32快速搭建FREERTOS 小编之前一直使用正点原子家的产品 xff0c 最近准备学习学习TOUCHGFX 要用到HAL 43 RTOS 原子家的使用起来不方便 于是琢磨着使用STM32CUBEMX直接生成FREERTOS
  • 使用DMA+SPI驱动Aliyun Things 上的ST7789H2 LCD屏幕

    目录 前言硬件CUBEMX时钟树GPIOSPI 代码部分LCD驱动中断服务函数测试代码现象 前言 1 xff1a 驱动程序参考自https blog csdn net BearPi article details 104311705 2 x
  • SLAM测试5-YGZ-Stereo-Inertial(GAAS双目视觉ygz -立体惯性SLAM)

    这篇主要测试GAAS开源无人机里用到的一种SLAM算法 xff0c 目的是先对该SLAM算法进行熟悉 xff0c 再开始入手GAAS视觉定位 GIThub上的代码地址为 xff1a https github com gaoxiang12 y
  • Linux之线程条件变量cond

    概念 xff1a 条件变量不是锁 xff0c 要和互斥量组合使用 条件变量就是生产者 生产 完成 xff0c 消费者才能 使用 xff0c 如果没有 产品 xff0c 消费者就会被条件变量cond阻塞等待生产者 生产 xff08 生产者与消
  • Linux之线程-信号量sem_*

    1 概念 信号量可理解为进化版的互斥锁 量 xff0c 允许多个线程访问共享资源 由于互斥锁的力度比较大 xff0c 如果希望在多个线程间对某一对象的部分数据进行共享 xff0c 使用互斥锁是没有办法实现的 xff0c 只能将整个数据对象锁