详解前驱图与PV操作
前驱图、PV操作
- 前驱图与PV操作的结合
- 例子:两个进程的同步问题
- 使用PV操作实现同步
- 前驱图的实际应用
- 更复杂的场景
- 示例
- 示例1:前驱图与PV操作的结合
- 1. 前驱图表示
- 2. 使用信号量(PV操作)实现同步
- 进程的执行逻辑:
- 3. 示例代码
- 4. 解释
- 5. 输出结果示例:
- 示例2:信号量分配
- 1. 示例背景
- 2. 信号量的分配
- 3. 信号量初始值:
- 4. 信号量的分配与操作
- 5. 信号量分配的详细步骤
- 6. 初始状态:
- 7. 信号量状态变化的解释:
- 8. 示例代码
- 9. 总结
前驱图(precedence graph)是一种用于表示并发系统中进程或事件之间依赖关系的有向图。在并发编程和同步领域,前驱图用于说明哪些操作必须先发生,哪些可以并行,哪些需要等待其他操作完成。它有助于理解多个进程如何协调执行,避免冲突或死锁等问题。
PV操作是一种经典的进程同步机制,它源自Dijkstra的信号量(Semaphore)理论。PV操作用于管理进程对共享资源的访问,防止并发进程中的竞争条件。P操作(Proberen,尝试)是对信号量减1的操作,如果信号量大于0,进程继续执行;否则,进程阻塞。V操作(Verhogen,增加)是对信号量加1的操作,释放资源,允许其他进程继续执行。
前驱图与PV操作的结合
在并发系统中,前驱图可以用来直观地描述进程的执行顺序和依赖关系,而PV操作用于实现这些依赖关系所需的同步。二者结合使用,能够通过信号量控制进程的执行,使其严格遵循前驱图的依赖顺序。
例子:两个进程的同步问题
假设有两个进程 A 和 B,它们分别进行以下步骤:
- 进程
A依次执行操作A1和A2 - 进程
B依次执行操作B1和B2
但是,它们之间有依赖关系:
A2必须在B1之后执行,也就是说,A2的前驱是B1。
用前驱图来表示这个依赖关系,图中会有一条从 B1 指向 A2 的有向边,表示 A2 依赖于 B1 的完成。
使用PV操作实现同步
我们可以使用信号量 S 来控制 A2 和 B1 之间的执行顺序。初始时,信号量 S=0,表示 A2 不能立即执行。
- 进程
B在执行完B1后,执行V(S)操作,表示B1完成并释放信号量。 - 进程
A在执行A2前,执行P(S)操作。由于S的初始值为0,A2会阻塞,直到B1完成并且S增加到1,A2才能执行。
前驱图的实际应用
前驱图和PV操作结合的关键在于:
- 前驱图:确定并发系统中事件的依赖顺序。
- PV操作:通过信号量实现这种顺序的同步控制。
通过这种方式,可以确保进程按照预期的顺序执行,避免资源竞争和死锁。例如,在数据库系统中,多个事务对相同数据的并发访问可以通过PV操作控制,保证数据一致性。在操作系统中,多个进程对共享内存的访问也可以通过前驱图建模,并结合PV操作确保正确的同步执行。
更复杂的场景
对于复杂的并发场景,前驱图可能会包含多个有向边,表示多个前驱事件。相应的PV操作可能涉及多个信号量。例如,如果 A3 依赖于 B2 和 C1 的完成,则在 A3 执行前需要等待两个信号量释放。
这种前驱图与PV操作结合的机制不仅限于简单的进程同步,还可以扩展到多进程通信、死锁预防、生产者-消费者问题等各种并发控制问题。
示例
示例1:前驱图与PV操作的结合
假设有三个进程 A、B 和 C,并且它们执行的任务如下:
- 进程 A:任务
A1-> 任务A2 - 进程 B:任务
B1-> 任务B2 - 进程 C:任务
C1-> 任务C2
任务之间有如下依赖关系:
A2必须在B1完成后才能执行。B2必须在C1完成后才能执行。
1. 前驱图表示
我们可以用前驱图表示这些依赖关系:
B1 → A2:表示A2依赖B1,即A2只有在B1完成后才能执行。C1 → B2:表示B2依赖C1,即B2只有在C1完成后才能执行。
这个前驱图可以画成:
B1 → A2
C1 → B2
2. 使用信号量(PV操作)实现同步
为了实现上述依赖关系,我们引入两个信号量:
- 信号量
S1,用于控制A2和B1的依赖。初始值为 0。 - 信号量
S2,用于控制B2和C1的依赖。初始值为 0。
初始情况下,两个信号量 S1 和 S2 都为 0,表示 A2 和 B2 都不能立即执行,必须等待相应的前驱任务完成。
进程的执行逻辑:
-
进程 A:
- 执行
A1。 - 执行
P(S1)操作(等待信号量S1),当S1 > 0时,才执行A2。
- 执行
-
进程 B:
- 执行
B1。 - 执行
V(S1)操作,释放信号量S1,允许A2执行。 - 执行
P(S2)操作(等待信号量S2),当S2 > 0时,才执行B2。
- 执行
-
进程 C:
- 执行
C1。 - 执行
V(S2)操作,释放信号量S2,允许B2执行。
- 执行
3. 示例代码
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>// 定义信号量
sem_t S1, S2;void* processA(void* arg) {printf("Process A: Executing A1\n");// 等待信号量S1,确保A2在B1之后执行sem_wait(&S1);printf("Process A: Executing A2 after B1\n");return NULL;
}void* processB(void* arg) {printf("Process B: Executing B1\n");// B1完成后,释放信号量S1,允许A2执行sem_post(&S1);// 等待信号量S2,确保B2在C1之后执行sem_wait(&S2);printf("Process B: Executing B2 after C1\n");return NULL;
}void* processC(void* arg) {printf("Process C: Executing C1\n");// C1完成后,释放信号量S2,允许B2执行sem_post(&S2);return NULL;
}int main() {// 初始化信号量sem_init(&S1, 0, 0); // S1 初始为0,A2不能立即执行sem_init(&S2, 0, 0); // S2 初始为0,B2不能立即执行pthread_t threadA, threadB, threadC;// 创建线程,模拟三个进程的执行pthread_create(&threadA, NULL, processA, NULL);pthread_create(&threadB, NULL, processB, NULL);pthread_create(&threadC, NULL, processC, NULL);// 等待线程完成pthread_join(threadA, NULL);pthread_join(threadB, NULL);pthread_join(threadC, NULL);// 销毁信号量sem_destroy(&S1);sem_destroy(&S2);return 0;
}
4. 解释
-
信号量的初始化:
sem_init(&S1, 0, 0)和sem_init(&S2, 0, 0)将信号量S1和S2初始化为 0。表示A2和B2不能立即执行,必须等待它们的依赖任务完成。 -
进程 A:
A1立即执行,执行到A2时,它会阻塞在sem_wait(&S1)处,直到B1完成并释放S1。 -
进程 B:
B1执行后调用sem_post(&S1),释放信号量S1,允许A2执行。B2需要等待C1完成,调用sem_wait(&S2)进行同步。 -
进程 C:
C1执行后调用sem_post(&S2),释放信号量S2,允许B2执行。
5. 输出结果示例:
Process B: Executing B1
Process A: Executing A1
Process C: Executing C1
Process A: Executing A2 after B1
Process B: Executing B2 after C1
示例2:信号量分配
1. 示例背景
假设我们有三个进程 A、B 和 C,它们执行的任务如下:
- 进程 A:
A1 -> A2 - 进程 B:
B1 -> B2 - 进程 C:
C1 -> C2
任务之间有以下依赖关系:
A2必须在B1完成后执行。B2必须在C1完成后执行。
2. 信号量的分配
我们将使用信号量 S1 和 S2 来同步这些依赖关系:
- 信号量
S1用于控制A2和B1之间的依赖。 - 信号量
S2用于控制B2和C1之间的依赖。
3. 信号量初始值:
S1 = 0:表示A2在B1完成之前不能执行。S2 = 0:表示B2在C1完成之前不能执行。
4. 信号量的分配与操作
-
初始状态:所有信号量的初始值为
0,表示依赖任务尚未完成,相关任务需要等待前驱任务完成后才能执行。 -
进程 A:
A1任务可以立即执行,不依赖任何其他任务。A2任务需要等待B1任务完成。我们使用sem_wait(S1)来阻塞A2的执行,直到B1释放信号量S1。
-
进程 B:
B1任务可以立即执行,不依赖其他任务。- 执行完
B1后,进程 B 调用sem_post(S1),将S1的值从0增加到1,释放信号量,使A2能够继续执行。 B2任务依赖于C1任务的完成,因此在B2任务执行前调用sem_wait(S2),阻塞B2,直到C1完成并释放信号量S2。
-
进程 C:
C1任务可以立即执行,不依赖任何其他任务。- 执行完
C1后,进程 C 调用sem_post(S2),将S2的值从0增加到1,允许B2执行。
5. 信号量分配的详细步骤
为了使这个过程更加清晰,让我们以具体的执行步骤和信号量状态变化为例:
6. 初始状态:
S1 = 0(A2需要等待B1完成)S2 = 0(B2需要等待C1完成)
| 操作 | 说明 | S1 信号量状态 | S2 信号量状态 |
|---|---|---|---|
A1 执行 | 无需等待,直接执行 | 0 | 0 |
B1 执行 | 无需等待,直接执行 | 0 | 0 |
sem_post(S1) | B1 完成,释放 S1,允许 A2 执行 | 1 | 0 |
A2 执行 | A2 依赖 B1,S1 = 1,可以执行 | 0 | 0 |
C1 执行 | 无需等待,直接执行 | 0 | 0 |
sem_post(S2) | C1 完成,释放 S2,允许 B2 执行 | 0 | 1 |
B2 执行 | B2 依赖 C1,S2 = 1,可以执行 | 0 | 0 |
7. 信号量状态变化的解释:
- 初始状态:
S1 = 0和S2 = 0,这意味着A2和B2不能立即执行。 B1完成后:B1执行完毕后,进程 B 通过sem_post(S1)将信号量S1增加到 1,表示A2可以执行。A2执行:A2检查S1是否大于 0,发现信号量已被释放,于是继续执行。C1完成后:C1执行完毕后,进程 C 通过sem_post(S2)将信号量S2增加到 1,表示B2可以执行。B2执行:B2检查S2是否大于 0,发现信号量已被释放,于是继续执行。
8. 示例代码
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>// 定义信号量
sem_t S1, S2;void* processA(void* arg) {printf("Process A: Executing A1\n");// 等待信号量S1,确保A2在B1之后执行sem_wait(&S1);printf("Process A: Executing A2 after B1\n");return NULL;
}void* processB(void* arg) {printf("Process B: Executing B1\n");// B1完成后,释放信号量S1,允许A2执行sem_post(&S1);// 等待信号量S2,确保B2在C1之后执行sem_wait(&S2);printf("Process B: Executing B2 after C1\n");return NULL;
}void* processC(void* arg) {printf("Process C: Executing C1\n");// C1完成后,释放信号量S2,允许B2执行sem_post(&S2);return NULL;
}int main() {// 初始化信号量sem_init(&S1, 0, 0); // S1 初始为0,A2不能立即执行sem_init(&S2, 0, 0); // S2 初始为0,B2不能立即执行pthread_t threadA, threadB, threadC;// 创建线程,模拟三个进程的执行pthread_create(&threadA, NULL, processA, NULL);pthread_create(&threadB, NULL, processB, NULL);pthread_create(&threadC, NULL, processC, NULL);// 等待线程完成pthread_join(threadA, NULL);pthread_join(threadB, NULL);pthread_join(threadC, NULL);// 销毁信号量sem_destroy(&S1);sem_destroy(&S2);return 0;
}
9. 总结
- **信
号量分配的关键在于将每个信号量与特定的任务依赖关系相关联,以确保进程按照正确的顺序执行。
在这个示例中:
S1控制A2任务在B1任务完成后执行。S2控制B2任务在C1任务完成后执行。
通过初始化信号量为 0,我们确保依赖任务(如 A2 和 B2)不会提前执行,只有在前驱任务完成并释放信号量时,依赖任务才能继续运行。
这种信号量的分配和使用可以广泛应用于多进程或多线程程序中,帮助有效地管理复杂的同步问题,避免竞争条件和不确定的执行顺序。
相关文章:
详解前驱图与PV操作
前驱图、PV操作 前驱图与PV操作的结合例子:两个进程的同步问题使用PV操作实现同步 前驱图的实际应用更复杂的场景示例示例1:前驱图与PV操作的结合1. 前驱图表示2. 使用信号量(PV操作)实现同步进程的执行逻辑: 3. 示例代…...
孩子来加拿大上学真的那么轻松吗?(上)
点击文末“阅读原文”即可参与节目互动 剪辑、音频 / 卷圈 运营 / SandLiu 卷圈 监制 / 姝琦 封面 / 姝琦Midjourney 产品统筹 / bobo 这是拼娃时代第三十一期节目,经过了一年的沉寂,拼娃时代在今年九月份终于恢复更新啦,JunJun老师也…...
【算法篇】二叉树类(1)(笔记)
目录 一、认识二叉树 1. 二叉树的种类 (1)满二叉树 (2)完全二叉树 (3)二叉搜索树 (4)平衡二叉搜索树 2. 二叉树的存储方式 3. 二叉树的遍历方式 4. 二叉树的定义 二、Leet…...
《C++无锁编程:解锁高性能并发的新境界》
在当今的软件开发领域,并发编程的重要性日益凸显。随着多核处理器的普及,开发者们越来越需要利用并发来提高程序的性能和响应速度。而 C作为一种强大的编程语言,提供了多种技术来实现无锁编程,从而在并发环境下获得更高的性能和更…...
系统架构设计师教程 第9章 9.5 软件可靠性测试 笔记
9.5 软件可靠性测试 ★★★☆☆ 9.5.1 软件可靠性测试概述 软件测试者可以使用很多方法进行软件测试,如按行为或结构来划分输入域的划分测试, 纯粹随机选择输入的随机测试,基于功能、路径、数据流或控制流的覆盖测试等。 软件可靠性测试由可…...
如何使用ssm实现校园体育赛事管理系统的设计与实现+vue
TOC ssm713校园体育赛事管理系统的设计与实现vue 绪论 课题背景 身处网络时代,随着网络系统体系发展的不断成熟和完善,人们的生活也随之发生了很大的变化。目前,人们在追求较高物质生活的同时,也在想着如何使自身的精神内涵得…...
CSS 中的文本相关属性(line - height、font、letter - 属性、text - 属性)
目录 非 VIP 用户可前往公众号回复“css”进行免费阅读 line - height属性 字号与行高的取值约定 行高与盒子高度的关系 font、letter -属性 、text -属性 font属性 letter -属性 text - 属性 非 VIP 用户可前往公众号回复“css”进行免费阅读 line - height属性 字号与…...
mobaxterm、vscode通过跳板机连接服务器
目标服务器:111.111.11.11 跳板机:100.100.10.10 1. mobaxterm通过跳板机连接服务器 1.1 目标服务器信息 1.2 跳板机信息 1.3 登录 点击登录,会输入密码,成功 参考:https://blog.csdn.net/qq_40636486/article/det…...
鸿萌数据恢复:iPhone 手机被盗后应采取哪些措施?警惕这些骗局
天津鸿萌科贸发展有限公司从事数据安全服务二十余年,致力于为各领域客户提供专业的数据恢复、数据备份解决方案与服务,并针对企业面临的数据安全风险,提供专业的相关数据安全培训。 丢失昂贵的 iPhone 不仅会造成较大的经济损失,还…...
为了学习Python熬夜部署了Jupyter Notebook 6.x
文章目录 Docker拉取并构建容器安装部署jupyter对Jupyter使用过程问题总结1 没有代码提示怎么办?2 如果想切换python版本了怎么办?3 想在jupyter里面使用vim怎么办? 遇见的问题参考文章 怎么说,今天在学习Python的时候,…...
docker-文件复制(docker cp:用于在Docker主机和容器之间拷贝文件或目录)
文章目录 1、把宿主机的文件复制到容器内部1.1、查询 宿主机 root 下的文件1.2、docker cp /root/anaconda-ks.cfg spzx-redis:/root1.3、查看 spzx-redis 容器 中/root目录下是否有 anaconda-ks.cfg 文件 2、把容器中的文件 复制 到宿主机中2.1、查看 spzx-redis 容器 / 下的文…...
guava里常用功能
guava 是 Google 提供的一个 Java 库,提供了很多实用的工具类和方法,可以帮助开发者更高效地编写代码。以下是一些常用的 Guava 工具类及其功能示例: 1. Lists 用于操作列表的工具类。 import com.google.common.collect.Lists;List<In…...
su 命令:一键切换用户身份、提高su命令安全性的建议
一、命令简介 su 命令是 Linux 和 Unix 系统中的一个实用工具,用于切换用户身份。它允许当前登录用户在不退出登录会话的情况下,切换到另一个用户的身份。通常,su 用于从普通用户切换到 root 用户,或从 root 用户切换到其他…...
观察者模式(发布-订阅模式)
用途: (1)可用于拦截过滤器 (2)订单创建成功后的一些后续逻辑(消息提醒,订单打印,物品打包等) (3)需要由统一调度中心调度的一系列任务等 消息…...
耦合微带线单元的网络参量和等效电路公式推导
文档下载链接:耦合微带线单元的网络参量和等效电路资源-CSDN文库https://download.csdn.net/download/lu2289504634/89583027笔者水平有限,错误之处欢迎留言! 一、耦合微带线奇偶模详细推导过程 二、2,4端口开路 三、2端口短路、3端口开路 四…...
elasticsearch的Ingest Attachment插件的使用总结
安装 Ingest Attachment 插件 确保 Elasticsearch 已安装: 首先,请确保你已经安装并运行了 Elasticsearch。可以通过访问 http://localhost:9200 来检查是否正常运行。 安装插件: 使用以下命令在 Elasticsearch 中安装 Ingest Attachment 插…...
SemiDrive E3 MCAL 开发系列(4) – Gpt 模块的使用
一、 概述 本文将会介绍SemiDrive E3 MCAL GPT模块的基本配置,并且会结合实际操作的介绍,帮助新手快速了解并掌握这个模块的使用,文中的 MCAL 是基于 PTG3.0 的版本,开发板是官方的 E3640 网关板。 二、 Gpt 模块的主要配置 …...
前端导出页面PDF
import html2canvas from html2canvas import { jsPDF } from jspdf import { Loading } from element-ui let downloadLoadingInstance// 导出页面为PDF格式---使用插件html2canvas和jspdf插件 export function exportPDF(fileName, node) {downloadLoadingInstance Loading.…...
Jenkins的安装
1.简介 官网:https://www.jenkins.io 中文文档:Jenkins Jenkins 是一个开源的持续集成(CI)工具,用于自动化构建、测试和部署软件项目。它提供了一个易于使用和可扩展的平台,帮助团队更高效地开发和交付软…...
初学51单片机之I2C总线与E2PROM
首先先推荐B站的I2C相关的视频I2C入门第一节-I2C的基本工作原理_哔哩哔哩_bilibili 看完视频估计就大概知道怎么操作I2C了,他的LCD1602讲的也很不错,把数据建立tsp和数据保持thd,比喻成拍照时候的摆pose和按快门两个过程,感觉还是…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...
接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...
【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...
大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...
前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...
