【Linux多线程编程】7. 线程锁(4)——信号量

news2024/12/23 13:42:15

前言

上篇文章Linux多线程编程】6. 线程锁(3)——条件变量 介绍了使用条件变量实现多线程同步的方式,而条件变量一般与互斥锁一同配合。本文介绍多线程同步的另一种方式——信号量,使用比条件变量简单,也用来解决生产者-消费者问题。

信号量

一个信号量 S 是个整型变量,它除了初始化外只能通过两个标准原子操作:wait ()signal() 来访问:

操作 wait() 也称为 P
操作 signal() 也称为 V

对于wait() 和 signal() 的定义可以如下

wait(S){
    while (S <= 0)
        ;// busy wait
    S--// 信号量S减
}
signal(S) {
    S++; // 信号量S加
}

由上代码不难看出,信号量看起来就是一个计数变量,wait对计数变量-1,signal对计数变量+1。
与普通定义一个变量,然后++/–的区别在于,信号量的操作是原子性的。

原子性:指事务的不可分割性,一个事务的所有操作要么不间断地全部被执行,要么一个也没有执行。

可以简单的将其理解为线程安全的,而对普通变量的加减之前我们说过是线程不安全的

操作系统通常区分计数信号量与二进制信号量。计数信号量的值不受限制,而二进制信号量的值只能为 0 或 1。因此,二进制信号量类似于互斥锁。事实上,在没有提供互斥锁的系统上,可以使用二进制信号量来提供互斥。
实际上,互斥锁我们可以看为只支持0/1变化的信号量

使用场景

信号量用于线程安全性的计数,通常这种功能被用在共享资源的访问上。
比如:
一家餐馆招了4个厨师,原料柜上放着3瓶酱油,我们可以为生菜加上一个信号量。
厨师A在t1时刻拿走一个生菜,此时生菜信号量-1,剩余2捆生菜;
厨师B在t2时刻拿走一个生菜,此时生菜信号量-1,剩余1捆生菜;
厨师C在t3时刻拿走一个生菜,此时生菜信号量-1,剩余0捆生菜;
厨师D在t4时刻想要拿走一个生菜,而此时生菜信号量为0,因此厨师D拿不到生菜,阻塞等待其他厨师释放已用的生菜;

看这个流程是不是跟之前说的互斥锁很像?区别在于:

信号量适用于多个线程在有多个相同资源时,资源数不为0,线程就可拿取资源;为0则等待
互斥锁是多个线程只有一个相同资源,其他线程抢先获取,另外的线程都阻塞等待。

因此,我们理解互斥锁就是只支持0/1计数的信号量。

主要函数

头文件 #define <semaphore.h>

  1. 创建信号量

int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:
sem:传出参数,函数返回后指向创建后的信号量。
pshared:控制信号量的类型,如果值为0,就表示这个信号量是当前进程的局部信号量,在当前进程的多个线程之间共享,否则信号量就可以在多个进程之间共享;
value:sem的初始值。
返回值:
调用成功时返回0,失败返回-1。

  1. 信号量值-1

int sem_wait(sem_t *sem);
参数:
sem:sem_init调用初始化的信号量。
返回值:
调用成功返回0,失败返回-1。

  1. 信号量的值+1

int sem_post(sem_t *sem);
参数:
sem:sem_init调用初始化的信号量。
返回值:
调用成功返回0,失败返回-1。

  1. 释放信号量

int sem_destroy(sem_t *sem);
参数:
销毁用sem_init初始化的信号量。
返回值:
调用成功返回0,失败返回-1。

代码示例

以生产者消费者模型为例,用信号量实现这个功能

#include <semaphare.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
pthread_mutex_t mutex;
sem_t psem;
sem_t csem;
struct Node{//创建链表
    int num;
    struct Node *next;
};
struct Node *head=NULL;//设置头结点为空
void *producer(void *arg)//生产者
{
    while(1)
    {
        sem_wait(&psem);//每进去一个车,公共车位就少一个,所以减一
        pthread_mutex_lock(&mutex);//上锁
        struct Node* newNode=(struct Node*)malloc(sizeof(struct Node));//开辟一块空间
        newNode->next=head;
        head=newNode;
        newNode->num=rand()%100;//取随机数
        printf("add node,num:%d,tid:%ld\n",newNode->num,pthread_self());
        pthread_mutex_unlock(&mutex);//解锁
        sem_post(&csem);//每次出去一个车,公共车位就多一个,所以加一
        usleep(100);
    }
        return NULL;

}
void *customer(void *arg)
{
    while(1)
    {
        sem_wait(&csem)//每次进去一个车,车位就减一
        pthread_mutex_lock(&mutex);//加锁
        struct Node *tmp=head;
        if(head!=NULL)
        {
            head=head->next;
            printf("del node,num:%d,tid:%ld\n",tmp->num,pthread_self());
            free(tmp);
            pthread_mutex_unlock(&mutex);//解锁
            sem_post(&psem);//每次出去一个车,车位就增加一个
        }
        return NULL;
    }
}
int main()
{
    pthread_mutex_init(&mutex,NULL);//初始化互斥锁
    sem_init(&psem,0,8);//初始化8个生产者空间
    sem_init(&csem,0,0);//初始化消费者,刚开始还没有消费,所以初始化为0
    
    pthread_t ptids[5],ctids[5];
    for(int i=0;i<5;i++)
    {
        pthread_create(&ptids[i],NULL,producer,NULL);//创建五个生产者线程
        pthread_create(&ctids[i],NULL,customer,NULL);//创建五个消费者线程
    }
    for(int i=0;i<5;i++)
    {
        pthread_join(ptids[i],NULL);//回收生产者子线程
        pthread_join(ctids[i],NULL);//回收消费者子线程
    }
    while(1)
    {
        sleep(10);
    }
    pthread_mutex_destroy(&mutex);//解除互斥锁
    sem_destroy(&psem);//释放生产者资源
    sem_destroy(&csem);//释放消费者资源
    pthread_exit(NULL);
    return 0;
}

在这里插入图片描述
通过观察我们会看到,有时候增加一个数字,就相应的减少一个数字,有时候同时增加几个数字,则按照栈操作顺序输出数字。

所以,运用信号量可以非常明显的实现了这个功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/154998.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Ansible】ansible Playbook

文章目录一、Ad-Hoc 的问题二、PlayBook 是什么三、YAML 学习1.yaml 特点2.基本语法四、 Playbook 的编写1.play 的定义2.Play 属性3.一个完整的剧本4. tasks 属性中任务的多种写法5.具有多个 play 的 playbook6. 如何对 playbook 进行语法校验下面校验的方法&#xff0c;只能校…

想要申请双软认证 这九大标准你满足吗

申请双软认证的优势有很多&#xff0c;最主要的就是能够节省企业的税收支出&#xff0c;减少成本&#xff0c;企业的利润就会增加&#xff0c;企业也能够发展得更好了。 要申请双软认证&#xff0c;企业就需要及时的了解双软认证的要求&#xff0c;不光是要有软件著作权&#…

编写jinjia2模板和角色部分 ansible(6)

目录 题目&#xff1a; 1、jinjia2模板&#xff1a; 编写hosts.j2&#xff0c;内容如下(主机名和ip地址使用变量)&#xff1a; &#xff08;1&#xff09;Welcome to 主机名 &#xff01;&#xff08;比如servera.lab.example.com&#xff09; My ip is ip地址. &#xff…

虹科新品 | 什么是光纤微动开关?(上)

01 什么是光纤微动开关 # 光纤微动开关结合了机械开关和光中断器的优点以及光纤波导的非金属性 光纤微动开关有一个确定的机械开关点&#xff0c;提供触觉反馈 光中断器没有电子机械部件&#xff0c;因此具有高可靠性 光纤是非金属的&#xff0c;它的信号损失几乎为零&#x…

苹果中国官网上线智能家居板块,蓝牙BLE在智能家居的应用

近期苹果中国区官网已上线单独的家居板块&#xff08;Apple Home&#xff09;。页面显示&#xff0c;家居板块主要分为三个品类&#xff1a;HomePod、Home App&#xff08;家庭 App&#xff09;以及智能家居配件。 据了解&#xff0c;智能家居单品均为第三方出品&#xff0c;虽…

OpenWrt 在没有80\443端口、不能dns验证的情况下为自己的域名申请免费ssl证书

适用范围 本方法适用于使用OpenWrt 在没有80\443端口、不能dns验证的情况下为自己的域名申请免费ssl证书。 提示&#xff1a; 1、如果你的网络的80或者443端口是开放状态&#xff0c;可直接使用https://letsencrypt.org/进行ssl申请&#xff0c;使用acme.sh脚本可快速完成。 2…

枚举类与注解

文章目录一、枚举类的使用枚举类的理解枚举类的定义Enum类的常用方法Enum类的实现接口二、注解&#xff08;Annotation&#xff09;说明如何自定义注解JDK提供的4种元注解通过反射获取注解信息&#xff08;到反射再讲&#xff09;JDK8注解的新特性每日一考一、枚举类的使用 枚…

Node.js教程笔记(二)模块化

学习目标 1、能够说出模块化的好处 2、能够知道CommonJS规定了哪些内容 3、能够说出NodeJS中模块的三大分类各是什么 4、能够使用npm管理包 5、能够了解什么是规范的包结构 6、能够了解模块的加载机制 目录 1、模块化的基本概念 2、NodeJS中模块的分类 3、npm与包 4…

Django项目使用wangeditor方法

一、环境&#xff1a; python&#xff1a; 3.8 Django &#xff1a;3.2.16 wangeditor&#xff1a; v4 &#xff08;官方文档地址&#xff1a;wangEditor&#xff09; JS文件下载&#xff1a;https://download.csdn.net/download/weixin_47401101/87379142 编译器&#xff1…

日志分析工具--花两天时间为自己开发个小工具值得吗?

这两天忙里偷闲整理开发了个小的日志分析工具&#xff0c;没错&#xff0c;是给自己使用的&#xff0c;工欲善其事必先利其器。 先说一下痛点&#xff0c; 1、经常会遇到需要在外网服务器上调试的情况&#xff0c;此时只能通过日志来调试信息。 2、当使用记事本打开日志文件后&…

windows驱动开发-WDF编程

文章目录前言WDF编程前的准备工作WDF编程创建驱动对象创建设备对象设备对象的回调函数链表操作驱动的测试代码前言 注&#xff1a;本文的完整代码见仓库 18-WDF-reflect 代码参考自&#xff1a;junjiexing/libredirect 我不是从事驱动开发的专业人员&#xff0c;打酱油需要用…

区块链Fabric-从入门到实战(二) Fabric环境搭建

Fabric ubuntu16虚拟机环境搭建Ubuntu16镜像ubuntu-16.04.3-server-amd64.iso &#xff08;如有需要&#xff0c;私信我&#xff09;修改软件源cd /etc/apt/sudo cp sources.list sources.list.baksudo gedit sources.listdeb-src http://mirror.neu.edu.cn/ubuntu/ xenial mai…

回望在CSDN的2022和展望全新的2023

前言时光如白驹过隙&#xff0c;这条消息提醒着我加入CSDN有一些日子了。4286天&#xff0c;只是一个普通的数字&#xff0c;却记录着与CSDN的每一天。过去太长&#xff0c;回望过去的2022&#xff0c;有很多的收获和成长。在C站结实了一些朋友&#xff0c;分享了自己在工作中的…

校园跑腿、校园脱单、代理、帮忙拿快递的微信小程序 基于SpringBoot、Mybatis-plus、mysql实现

一、文件夹说明 代码下载 地址:校园跑腿、校园脱单、代理、帮忙拿快递的微信小程序 server 后端项目 project&#xff1a; 项目 CBD&#xff1a; 校园跑腿服务&#xff08;校园CBD中心&#xff09; server-app: 小程序apiserver-pc: 小程序后台管理service-cgs-base-service:…

RK3399平台开发系列讲解(CPU篇)CPUFreq 中央处理器频率调节技术

🚀返回专栏总目录 文章目录 一、CPUFreq组成二、设备树配置沉淀、分享、成长,让自己和他人都能有所收获!😄 📢中央处理器频率调节(Central Processing Unit frequency,CPUFreq)技术可以降低ARM芯片的功耗,例如在系统对任务压力较小时,通过调整处理器工作频率与输入…

Javascript判断点是否在多边型内

射线法Ray-casting Algorithm算法检查点point是否在多边形里面。用法&#xff1a;insidePolygon([[x1,y1],[x2,y2],[x3,y3]...], [x , y])参数说明&#xff1a;polygon多边形坐标集合&#xff0c;格式为[[x1,y1],[x2,y2],[x3,y3]...]。point 测试点坐标, 格式为[x , y]。返回tr…

技术分享| 视频监控融合方案

视频监控系统在各行业应用广泛&#xff0c;从早期的只是简单的实现通过视频记录监控区域的情况&#xff0c;到现在的监控侦测、智能报警、融合通信等功能&#xff0c;视频监控的作用已经不是简单的记录&#xff0c;分布在各地的视频监控摄像头可以通过复杂的软件算法实现更多智…

数据分析的尽头不是可视化,而是行动!行动!行动!

Kyligence Zen 联动飞书&#xff0c;支持一键推送指标&#xff0c;在飞书就能追踪关键指标的最新动态&#xff1b;指标对齐目标&#xff0c;目标拆解为飞书任务&#xff0c;实现从指标洞察到行动的丝滑闭环&#xff01; 指标是衡量目标的量化参数&#xff0c;也是企业将战略目标…

PC端网页特效:轮播图

轮播图 功能需求&#xff1a; 鼠标经过轮播图模块&#xff0c;左右按钮显示&#xff0c;离开隐藏左右按钮。点击右侧按钮一次&#xff0c;图片往左播放一张&#xff0c; 左侧按钮同理。图片播放的同时&#xff0c;下面小圆圈模块跟随一起变化。点击小圆圈&#xff0c;可以播放…

2023年系统集成项目管理工程师报考条件及时间安排

一、报考条件 二、考试时间安排 集成考试一年会考2次&#xff0c;上半年一次、下半年一次 考试内容&#xff1a; 三、考试知识点分布&#xff1a; 给出一点点中项备考攻略 中级我敢说是好考的&#xff0c;题目也不难&#xff0c;主要弄清楚47个过程的输入输出&#xff0c;还有…