多线程与信号量简介

信号量与 PV 操作

计算机中信号量的本质是整数,数值表示可用的资源数量

P 操作 (Passeren => 通过, 原子操作)

  • 若信号量 == 0,当前任务阻塞 (进入信号量等待队列)
  • 若信号量 > 0,则:将信号量数值减一,当前任务继续执行

V 操作 (Vrijgeven => 释放, 原子操作)

  • 将信号量数值加一
  • 若 信号量 > 0,则:唤醒阻塞的其它任务,当前任务继续执行

信号量与 PV 操作注意事项

程序中的 PV 操作必须成对出现 (P 操作 => 临界区 => V 操作)

信号量初始值一般为 1 (初始值与相应资源数量相关)

信号量也看做特殊的互斥量 (信号量初始值为 1 时,退化为互斥量)

若 信号量 == S && S > 0,则:可进行 P 操作并且不阻塞的次数为 S

信号量模拟用法示例

Linux 中的信号量

下面的程序输出什么?为什么?

test1.c

#define _GNU_SOURCE     /* To get pthread_getattr_np() declaration */
#define _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200809L
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <memory.h>
#include <semaphore.h>void* customer_thread(void* arg)
{   printf("thread begin %ld\n", pthread_self());sem_wait(arg);printf("thread end %ld\n", pthread_self());return NULL;
}int main()
{pthread_t t = {0};sem_t sem = {0};int i = 0;int v = 0;sem_init (&sem, PTHREAD_PROCESS_PRIVATE, 1);sem_getvalue(&sem, &v);printf("sem = %d\n", v);for(i=0; i<5; i++){pthread_create(&t, NULL, customer_thread, &sem);}sleep(5);sem_getvalue(&sem, &v);printf("sem = %d\n", v);printf("End!\n");return 0;
}

第 28 行,初始化信号量,将信号量的初始值设置为 1,那么这里的信号量等同与互斥锁

第 36 行,主线程创建了 5 个子线程,子线程通过 sem_wait() 去获取信号量

第 41 行,主线程通过 sem_getvalue() 来获取当前信号量的值

由于信号量的初始值为 1,所以只能有一个子线程获取到信号量,其它的线程来获取信号量时,发现信号量的值为 0,就会阻塞等待信号量被释放

程序运行结果如下图所示:

只有一个子线程获取到了信号量,最后信号量的值为 0

生产消费者问题示例

信号量初体验

我们使用信号量来解决生产者消费者问题

test3.c

#define _GNU_SOURCE     /* To get pthread_getattr_np() declaration */
#define _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200809L
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <memory.h>
#include <semaphore.h>typedef struct
{sem_t r;sem_t w;
} Sem;void* customer_thread(void* arg)
{   Sem* s = arg;  sleep(1);while( 1 ){sem_wait(&s->r);printf("%s : get\n", __FUNCTION__);sem_post(&s->w);}return NULL;
}int main()
{pthread_t t = {0};Sem sem = {0};sem_init (&sem.r, PTHREAD_PROCESS_PRIVATE, 0);sem_init (&sem.w, PTHREAD_PROCESS_PRIVATE, 1);pthread_create(&t, NULL, customer_thread, &sem);printf("Hello World!\n");while( 1 ){sem_wait(&sem.w);printf("%s : set\n", __FUNCTION__);sem_post(&sem.r);sleep(3);}printf("End!\n");return 0;
}

该程序中使用的两个信号量,信号量 w 和信号量 r,信号量 w 的初始值为 1,信号量 r 的初始值为 0

只有主线程写完以后,信号量 r 的值会加一,子线程才能去读;子线程读完以后,信号量 w 的值会加一,主线程才能去写;会一直重复这个流程

程序运行结果如下图所示:

思考

进程之间是否需要进行同步与互斥?

多进程场景

多个进程共享一段内存,即:读写共享内存

此时共享内存的访问就是临界区访问

因此,需要对临界区进行保护 (防止多个进程同时写操作)

问题:是否存在跨进程使用的互斥量?

多进程内存共享

Linux 中的跨进程信号量

多进程内存共享

多进程与信号量

test5.c

#define _GNU_SOURCE     /* To get pthread_getattr_np() declaration */
#define _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200809L
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <memory.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/wait.h>#define SEM_NAME  "delphi_tang"
#define PATH_NAME "/home/book/Documents"
#define PROJ_ID   199int get_shared_memory(key_t k)
{int ret = shmget(k, 0, 0);if( ret == -1 ){ret = shmget(k, 128,  IPC_CREAT | IPC_EXCL | S_IRWXU);} return ret;
}sem_t* get_sem(int v)
{sem_t* ret = sem_open(SEM_NAME, 0);if( ret == SEM_FAILED ){ret = sem_open(SEM_NAME, O_CREAT | O_EXCL, S_IRWXU, v);}return ret;
}int main(int argc, char* argv[])
{key_t k = ftok(PATH_NAME, PROJ_ID);  char* shmaddr = NULL;sem_t* sem = NULL;int shmid = get_shared_memory(k);printf("shmid = %d\n", shmid);if( shmid == -1 ){printf("shmget error\n");exit(1);} sem = get_sem(1);if( sem ){printf("sem is %p\n", sem);}shmaddr = shmat(shmid, NULL, 0);while( (argc > 1) && shmaddr ){static int i = 0;if( strcmp(argv[1], "write") == 0 ){sem_wait(sem);sprintf(shmaddr, "shared string %d", i++);printf("write: %s\n", shmaddr);sem_post(sem);usleep(1000 * 1000);}else if( strcmp(argv[1], "read") == 0 ){sem_wait(sem);printf("read: %s\n", shmaddr);sem_post(sem);usleep(250 * 1000);}else{break;}}printf("Press any key to finish process...\n");system("read -s -n 1");shmctl(shmid, IPC_RMID, NULL); sem_close(sem);return 0;
}

第 50 行,使用 ftok() 函数用于生成System V IPC(Inter-Process Communication,进程间通信)对象(如信号量、消息队列和共享内存)的键(key)

第 53 行,get_shared_memory() 函数调用了 shmget() 函数,shmget() 是一个Linux系统调用函数,用于创建一个新的共享内存段(segment)或获取一个已存在的共享内存段。这个函数会返回一个整数类型的共享内存标识符(ID),用于在后续的系统调用中引用共享内存段

第 63 行,get_sem() 函数调用了 sem_open() 函数,sem_open() 用于打开或创建一个命名信号量

第 70 行,shmat() 是一个Linux系统调用函数,用于将一个共享内存段附加到当前进程的地址空间。这使得进程可以通过指针访问共享内存段中的数据。shmat() 函数通常在调用 shmget() 函数后使用,以便将获得的共享内存段附加到进程的地址空间

第 72 行 - 95 行,通过信号量来访问多进程中的共享内存

程序运行结果如下图所示:

运行了 2 个进程,一个进程写共享内存,一个进程读取共享内存,信号量用于同步多进程间共享内存的读写

跨进程的信号量是通过文件的方式实现的,创建出的跨进程信号量的文件位置存放在 /dev/shm/目录下面

Linux 信号量的注意事项

信号量之间不能相互初始化,也不能相互赋值 (行为未定义)

跨进程信号量通过文件的方式实现,因此涉及读写权限

sem_close() 仅仅关闭信号量,信号量未删除

sem_unlink() 延迟删除信号量 (/dev/shm/)

  • 即:所有访问信号量的信号量结束后,信号量才被删除

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

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

相关文章

你知道什么是防抖和节流吗?

目录 1 先举个例子 2 使用场景 3 代码实现 3.1 防抖的实现 3.2 节流的实现 1 先举个例子 防抖&#xff0c;更像是坐电梯&#xff0c;早上眼看9点了&#xff0c;都着急坐电梯上去打卡&#xff0c;但眼看电梯要关了&#xff0c;进来一个人&#xff0c;等几秒&#xff0c;…

uniapp乡村社区户籍问外来人员管理系统 微信小程序python+java+node.js+php

基于微信小程序的外来人员管理系统项目的概述设计分析&#xff0c;主要内容有的私教预约平台系统平台的具体分析&#xff0c;进行数据库的是设计&#xff0c;数据采用MySQL数据库&#xff0c;并且对于系统的设计采用比较人性化的操作设计&#xff0c;对于系统出现的错误信息可以…

信创 | 信创产业人才需求与培养机制:优化策略与实践探索

信创产业的人才需求与培养机制面临着多方面的挑战和机遇。首先&#xff0c;信创产业的快速发展带来了巨大的人才需求&#xff0c;但目前人才培养供给与企业发展需求之间存在不匹配的问题。这种不匹配主要表现在课程体系不健全、产教融合不够深入、校企联动性不足以及职业培训市…

探索动态内存开辟的奥秘

✨✨欢迎&#x1f44d;&#x1f44d;点赞☕️☕️收藏✍✍评论 个人主页&#xff1a;秋邱博客 所属栏目&#xff1a;C语言 前言 开始之前&#xff0c;我们先来了解一下C/C中程序内存区域划分。 在C/C程序中&#xff0c;内存区域通常被划分为以下几个部分&#xff1a; 1.栈&…

第16章 基于结构的测试技术(白盒测试技术)

一、静态测试技术 &#xff08;一&#xff09;概述 不运行程序代码的情况下&#xff0c;通过质量准则或其他准则对测试项目进行检查的测试类型&#xff0c;人工或工具检查。 1、代码检查 2、编码规则检查 3、静态分析 静态分析概述 不需要执行程序 控制流分析 通过生成…

短视频矩阵系统源码==3年源头开发

一 短视频矩阵系统具备以下特点: 1.内容管理功能:用户可以在系统中多账号托管 一次性上传、编辑和发布多个短视频平台的内容&#xff0c;无需在每个平台上重复操作&#xff0c;从而提高工作效率并保持内容的一致性和高质量 2.批量剪辑视频:系统支持上传批量素材管理剪辑 视频…

Visual studio调试技巧

Visual studio调试技巧 bug是什么&#xff1f;Debug和ReleaseDebugRelease 如何调试VS调试快捷键调试过程中查看程序信息查看临时变量的值查看内存信息查看调用堆栈查看汇编信息查看寄存器信息 编译常见错误编译型错误链接型错误运行时错误 bug是什么&#xff1f; bug的英文释…

SAM:Segment Anything Model

论文&#xff08;ICCV&#xff0c;fackbook&#xff09;&#xff1a;Segment Anything 源码&#xff1a; https://github.com/facebookresearch/segment-anything demo&#xff1a;Segment Anything | Meta AI (segment-anything.com) 一、摘要 本文介绍了“Segment Anything…

活动回顾 | 春起潮涌——硬件驱动的量化交易与AI

4月20日&#xff0c;华锐技术ACLUB联合AMD在上海举办了“春起潮涌——硬件驱动的量化交易与AI”沙龙活动&#xff0c;会议围绕FPGA硬件加速、CPU&网卡调优、AI技术应用等展开&#xff0c;近50位量化IT与分享嘉宾一起探讨硬件技术在量化交易和AI领域的应用和创新。 FPGA在交…

【C++第七课-string用法】

这里写自定义目录标题 string的初步介绍sring的构造函数string的构造函数-重点掌握无参的构造函数用常量字符串来初始化拷贝构造 string的构造函数-非重点掌握拷贝字符串str从pos位置开始的len个字符拷贝字符串s的前n个字符用n个c去初始化 string的赋值string的遍历和访问下标[…

爬虫自动化之drissionpage实现随时切换代理ip

目录 一、视频二、dp首次启动设置代理三、dp利用插件随时切换代理一、视频 视频直接点击学习SwitchyOmega插件使用其它二、dp首次启动设置代理 from DrissionPage import ChromiumPage, ChromiumOptions from loguru

matplotlib/seaborn 绘图可视化全面总结

1.概述 使用Matplotlib 绘图实现可视化时&#xff0c;会面临不同的需求有所调整&#xff0c;本文档重点对绘图过程中产生的一些小众需求进行全面总结&#xff0c;其他任务时可以随时即抽即用。 2.绘图 2.1 一般绘图 plt.figure() 参数设置说明 matplotlib.pyplot.figure( …

C++设计模式-创建型设计模式

设计模式 设计模式是什么 设计模式是指在软件开发中&#xff0c;经过验证的&#xff0c;用于解决在特定环境下&#xff0c;重复出现的&#xff0c;特定问题的解决方案&#xff1b;其实就是解决问题的固定套路。但是要慎用设计模式&#xff0c;有一定的工程代码量之后用它比较…

【论文阅读】Tutorial on Diffusion Models for Imaging and Vision

1.The Basics: Variational Auto-Encoder 1.1 VAE Setting 自动编码器有一个输入变量x和一个潜在变量z Example. 获得图像的潜在表现并不是一件陌生的事情。回到jpeg压缩&#xff0c;使用离散余弦变换&#xff08;dct&#xff09;基φn对图像的底层图像/块进行编码。如果你给…

先电2.4的openstack搭建

先电2.4版本的openstack&#xff0c;前期虚拟机部署参考上一篇2.2版本&#xff0c;基本步骤是一样的&#xff0c;准备两个镜像文件CentOS-7.5-x86_64-DVD-1804.iso&#xff0c;XianDian-IaaS-V2.4.iso [rootcontroller ~]# cat /etc/sysconfig/network-scripts/ifcfg-eno16777…

华为OD机试 - 小扇和小船的数字游戏 - 二进制(Java 2024 C卷 200分)

华为OD机试 2024C卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测试…

论文笔记(四十五)Attention Is All You Need

Attention Is All You Need 文章概括摘要1. 介绍2. 背景3. 模型架构3.1 编码器和解码器堆栈3.2 Attention3.2.1 按比例点积Attention3.2.2 Multi-Head Attention3.2.3 注意力在模型中的应用 3.3 定位前馈网络3.4 嵌入与 Softmax3.5 位置编码 4 为什么 Self-Attention5. Trainin…

ssm104园区停车管理系统+jsp

园区停车管理系统的设计与实现 摘 要 网络技术和计算机技术发展至今&#xff0c;已经拥有了深厚的理论基础&#xff0c;并在现实中进行了充分运用&#xff0c;尤其是基于计算机运行的软件更是受到各界的关注。加上现在人们已经步入信息时代&#xff0c;所以对于信息的宣传和管…

P9422 [蓝桥杯 2023 国 B] 合并数列

P9422 [蓝桥杯 2023 国 B] 合并数列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 用队列即可 当两个队列队首&#xff1a;a b &#xff0c;弹出 当a < b&#xff0c;把a加给其后一个元素&#xff0c;弹出a 当b < a&#xff0c;把b加给其后一个元素&#xff0c;弹出…

[初阶数据结构】时间复杂度与空间复杂度

前言 &#x1f4da;作者简介&#xff1a;爱编程的小马&#xff0c;正在学习C/C&#xff0c;Linux及MySQL。 &#x1f4da;本文收录于初阶数据结构系列&#xff0c;本专栏主要是针对时间、空间复杂度&#xff0c;顺序表和链表、栈和队列、二叉树以及各类排序算法&#xff0c;持续…