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

详解前驱图与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操作用于实现这些依赖关系所需的同步。二者结合使用,能够通过信号量控制进程的执行,使其严格遵循前驱图的依赖顺序。

例子:两个进程的同步问题

假设有两个进程 AB,它们分别进行以下步骤:

  • 进程 A 依次执行操作 A1A2
  • 进程 B 依次执行操作 B1B2

但是,它们之间有依赖关系:

  • A2 必须在 B1 之后执行,也就是说,A2 的前驱是 B1

用前驱图来表示这个依赖关系,图中会有一条从 B1 指向 A2 的有向边,表示 A2 依赖于 B1 的完成。

使用PV操作实现同步

我们可以使用信号量 S 来控制 A2B1 之间的执行顺序。初始时,信号量 S=0,表示 A2 不能立即执行。

  1. 进程 B 在执行完 B1 后,执行 V(S) 操作,表示 B1 完成并释放信号量。
  2. 进程 A 在执行 A2 前,执行 P(S) 操作。由于 S 的初始值为0,A2 会阻塞,直到 B1 完成并且 S 增加到1,A2 才能执行。

前驱图的实际应用

前驱图和PV操作结合的关键在于:

  • 前驱图:确定并发系统中事件的依赖顺序。
  • PV操作:通过信号量实现这种顺序的同步控制。

通过这种方式,可以确保进程按照预期的顺序执行,避免资源竞争和死锁。例如,在数据库系统中,多个事务对相同数据的并发访问可以通过PV操作控制,保证数据一致性。在操作系统中,多个进程对共享内存的访问也可以通过前驱图建模,并结合PV操作确保正确的同步执行。

更复杂的场景

对于复杂的并发场景,前驱图可能会包含多个有向边,表示多个前驱事件。相应的PV操作可能涉及多个信号量。例如,如果 A3 依赖于 B2C1 的完成,则在 A3 执行前需要等待两个信号量释放。

这种前驱图与PV操作结合的机制不仅限于简单的进程同步,还可以扩展到多进程通信、死锁预防、生产者-消费者问题等各种并发控制问题。

示例

示例1:前驱图与PV操作的结合

假设有三个进程 ABC,并且它们执行的任务如下:

  • 进程 A:任务 A1 -> 任务 A2
  • 进程 B:任务 B1 -> 任务 B2
  • 进程 C:任务 C1 -> 任务 C2

任务之间有如下依赖关系:

  1. A2 必须在 B1 完成后才能执行。
  2. B2 必须在 C1 完成后才能执行。

1. 前驱图表示

我们可以用前驱图表示这些依赖关系:

  • B1 → A2:表示 A2 依赖 B1,即 A2 只有在 B1 完成后才能执行。
  • C1 → B2:表示 B2 依赖 C1,即 B2 只有在 C1 完成后才能执行。

这个前驱图可以画成:

B1 → A2
C1 → B2

2. 使用信号量(PV操作)实现同步

为了实现上述依赖关系,我们引入两个信号量:

  • 信号量 S1,用于控制 A2B1 的依赖。初始值为 0。
  • 信号量 S2,用于控制 B2C1 的依赖。初始值为 0。

初始情况下,两个信号量 S1S2 都为 0,表示 A2B2 都不能立即执行,必须等待相应的前驱任务完成。

进程的执行逻辑:
  • 进程 A

    1. 执行 A1
    2. 执行 P(S1) 操作(等待信号量 S1),当 S1 > 0 时,才执行 A2
  • 进程 B

    1. 执行 B1
    2. 执行 V(S1) 操作,释放信号量 S1,允许 A2 执行。
    3. 执行 P(S2) 操作(等待信号量 S2),当 S2 > 0 时,才执行 B2
  • 进程 C

    1. 执行 C1
    2. 执行 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) 将信号量 S1S2 初始化为 0。表示 A2B2 不能立即执行,必须等待它们的依赖任务完成。

  • 进程 AA1 立即执行,执行到 A2 时,它会阻塞在 sem_wait(&S1) 处,直到 B1 完成并释放 S1

  • 进程 BB1 执行后调用 sem_post(&S1),释放信号量 S1,允许 A2 执行。B2 需要等待 C1 完成,调用 sem_wait(&S2) 进行同步。

  • 进程 CC1 执行后调用 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. 示例背景

假设我们有三个进程 ABC,它们执行的任务如下:

  • 进程 AA1 -> A2
  • 进程 BB1 -> B2
  • 进程 CC1 -> C2

任务之间有以下依赖关系:

  1. A2 必须在 B1 完成后执行。
  2. B2 必须在 C1 完成后执行。

2. 信号量的分配

我们将使用信号量 S1S2 来同步这些依赖关系:

  1. 信号量 S1 用于控制 A2B1 之间的依赖。
  2. 信号量 S2 用于控制 B2C1 之间的依赖。
3. 信号量初始值:
  • S1 = 0:表示 A2B1 完成之前不能执行。
  • S2 = 0:表示 B2C1 完成之前不能执行。

4. 信号量的分配与操作

  1. 初始状态:所有信号量的初始值为 0,表示依赖任务尚未完成,相关任务需要等待前驱任务完成后才能执行。

  2. 进程 A

    • A1 任务可以立即执行,不依赖任何其他任务。
    • A2 任务需要等待 B1 任务完成。我们使用 sem_wait(S1) 来阻塞 A2 的执行,直到 B1 释放信号量 S1
  3. 进程 B

    • B1 任务可以立即执行,不依赖其他任务。
    • 执行完 B1 后,进程 B 调用 sem_post(S1),将 S1 的值从 0 增加到 1,释放信号量,使 A2 能够继续执行。
    • B2 任务依赖于 C1 任务的完成,因此在 B2 任务执行前调用 sem_wait(S2),阻塞 B2,直到 C1 完成并释放信号量 S2
  4. 进程 C

    • C1 任务可以立即执行,不依赖任何其他任务。
    • 执行完 C1 后,进程 C 调用 sem_post(S2),将 S2 的值从 0 增加到 1,允许 B2 执行。

5. 信号量分配的详细步骤

为了使这个过程更加清晰,让我们以具体的执行步骤和信号量状态变化为例:

6. 初始状态:
  • S1 = 0A2 需要等待 B1 完成)
  • S2 = 0B2 需要等待 C1 完成)
操作说明S1 信号量状态S2 信号量状态
A1 执行无需等待,直接执行00
B1 执行无需等待,直接执行00
sem_post(S1)B1 完成,释放 S1,允许 A2 执行10
A2 执行A2 依赖 B1S1 = 1,可以执行00
C1 执行无需等待,直接执行00
sem_post(S2)C1 完成,释放 S2,允许 B2 执行01
B2 执行B2 依赖 C1S2 = 1,可以执行00
7. 信号量状态变化的解释:
  1. 初始状态S1 = 0S2 = 0,这意味着 A2B2 不能立即执行。
  2. B1 完成后B1 执行完毕后,进程 B 通过 sem_post(S1) 将信号量 S1 增加到 1,表示 A2 可以执行。
  3. A2 执行A2 检查 S1 是否大于 0,发现信号量已被释放,于是继续执行。
  4. C1 完成后C1 执行完毕后,进程 C 通过 sem_post(S2) 将信号量 S2 增加到 1,表示 B2 可以执行。
  5. 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,我们确保依赖任务(如 A2B2)不会提前执行,只有在前驱任务完成并释放信号量时,依赖任务才能继续运行。

这种信号量的分配和使用可以广泛应用于多进程或多线程程序中,帮助有效地管理复杂的同步问题,避免竞争条件和不确定的执行顺序。

相关文章:

详解前驱图与PV操作

前驱图、PV操作 前驱图与PV操作的结合例子&#xff1a;两个进程的同步问题使用PV操作实现同步 前驱图的实际应用更复杂的场景示例示例1&#xff1a;前驱图与PV操作的结合1. 前驱图表示2. 使用信号量&#xff08;PV操作&#xff09;实现同步进程的执行逻辑&#xff1a; 3. 示例代…...

孩子来加拿大上学真的那么轻松吗?(上)

点击文末“阅读原文”即可参与节目互动 剪辑、音频 / 卷圈 运营 / SandLiu 卷圈 监制 / 姝琦 封面 / 姝琦Midjourney 产品统筹 / bobo 这是拼娃时代第三十一期节目&#xff0c;经过了一年的沉寂&#xff0c;拼娃时代在今年九月份终于恢复更新啦&#xff0c;JunJun老师也…...

【算法篇】二叉树类(1)(笔记)

目录 一、认识二叉树 1. 二叉树的种类 &#xff08;1&#xff09;满二叉树 &#xff08;2&#xff09;完全二叉树 &#xff08;3&#xff09;二叉搜索树 &#xff08;4&#xff09;平衡二叉搜索树 2. 二叉树的存储方式 3. 二叉树的遍历方式 4. 二叉树的定义 二、Leet…...

《C++无锁编程:解锁高性能并发的新境界》

在当今的软件开发领域&#xff0c;并发编程的重要性日益凸显。随着多核处理器的普及&#xff0c;开发者们越来越需要利用并发来提高程序的性能和响应速度。而 C作为一种强大的编程语言&#xff0c;提供了多种技术来实现无锁编程&#xff0c;从而在并发环境下获得更高的性能和更…...

系统架构设计师教程 第9章 9.5 软件可靠性测试 笔记

9.5 软件可靠性测试 ★★★☆☆ 9.5.1 软件可靠性测试概述 软件测试者可以使用很多方法进行软件测试&#xff0c;如按行为或结构来划分输入域的划分测试&#xff0c; 纯粹随机选择输入的随机测试&#xff0c;基于功能、路径、数据流或控制流的覆盖测试等。 软件可靠性测试由可…...

如何使用ssm实现校园体育赛事管理系统的设计与实现+vue

TOC ssm713校园体育赛事管理系统的设计与实现vue 绪论 课题背景 身处网络时代&#xff0c;随着网络系统体系发展的不断成熟和完善&#xff0c;人们的生活也随之发生了很大的变化。目前&#xff0c;人们在追求较高物质生活的同时&#xff0c;也在想着如何使自身的精神内涵得…...

CSS 中的文本相关属性(line - height、font、letter - 属性、text - 属性)

目录 非 VIP 用户可前往公众号回复“css”进行免费阅读 line - height属性 字号与行高的取值约定 行高与盒子高度的关系 font、letter -属性 、text -属性 font属性 letter -属性 text - 属性 非 VIP 用户可前往公众号回复“css”进行免费阅读 line - height属性 字号与…...

mobaxterm、vscode通过跳板机连接服务器

目标服务器&#xff1a;111.111.11.11 跳板机&#xff1a;100.100.10.10 1. mobaxterm通过跳板机连接服务器 1.1 目标服务器信息 1.2 跳板机信息 1.3 登录 点击登录&#xff0c;会输入密码&#xff0c;成功 参考&#xff1a;https://blog.csdn.net/qq_40636486/article/det…...

鸿萌数据恢复:iPhone 手机被盗后应采取哪些措施?警惕这些骗局

天津鸿萌科贸发展有限公司从事数据安全服务二十余年&#xff0c;致力于为各领域客户提供专业的数据恢复、数据备份解决方案与服务&#xff0c;并针对企业面临的数据安全风险&#xff0c;提供专业的相关数据安全培训。 丢失昂贵的 iPhone 不仅会造成较大的经济损失&#xff0c;还…...

为了学习Python熬夜部署了Jupyter Notebook 6.x

文章目录 Docker拉取并构建容器安装部署jupyter对Jupyter使用过程问题总结1 没有代码提示怎么办&#xff1f;2 如果想切换python版本了怎么办&#xff1f;3 想在jupyter里面使用vim怎么办&#xff1f; 遇见的问题参考文章 怎么说&#xff0c;今天在学习Python的时候&#xff0c…...

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 库&#xff0c;提供了很多实用的工具类和方法&#xff0c;可以帮助开发者更高效地编写代码。以下是一些常用的 Guava 工具类及其功能示例&#xff1a; 1. Lists 用于操作列表的工具类。 import com.google.common.collect.Lists;List<In…...

su 命令:一键切换用户身份、提高su命令安全性的建议

一、命令简介 ​su ​命令是 Linux 和 Unix 系统中的一个实用工具&#xff0c;用于切换用户身份。它允许当前登录用户在不退出登录会话的情况下&#xff0c;切换到另一个用户的身份。通常&#xff0c;su ​用于从普通用户切换到 root 用户&#xff0c;或从 root 用户切换到其他…...

观察者模式(发布-订阅模式)

用途&#xff1a; &#xff08;1&#xff09;可用于拦截过滤器 &#xff08;2&#xff09;订单创建成功后的一些后续逻辑&#xff08;消息提醒&#xff0c;订单打印&#xff0c;物品打包等&#xff09; &#xff08;3&#xff09;需要由统一调度中心调度的一系列任务等 消息…...

耦合微带线单元的网络参量和等效电路公式推导

文档下载链接&#xff1a;耦合微带线单元的网络参量和等效电路资源-CSDN文库https://download.csdn.net/download/lu2289504634/89583027笔者水平有限&#xff0c;错误之处欢迎留言&#xff01; 一、耦合微带线奇偶模详细推导过程 二、2,4端口开路 三、2端口短路、3端口开路 四…...

elasticsearch的Ingest Attachment插件的使用总结

安装 Ingest Attachment 插件 确保 Elasticsearch 已安装&#xff1a; 首先&#xff0c;请确保你已经安装并运行了 Elasticsearch。可以通过访问 http://localhost:9200 来检查是否正常运行。 安装插件&#xff1a; 使用以下命令在 Elasticsearch 中安装 Ingest Attachment 插…...

SemiDrive E3 MCAL 开发系列(4) – Gpt 模块的使用

一、 概述 本文将会介绍SemiDrive E3 MCAL GPT模块的基本配置&#xff0c;并且会结合实际操作的介绍&#xff0c;帮助新手快速了解并掌握这个模块的使用&#xff0c;文中的 MCAL 是基于 PTG3.0 的版本&#xff0c;开发板是官方的 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.简介 官网&#xff1a;https://www.jenkins.io 中文文档&#xff1a;Jenkins Jenkins 是一个开源的持续集成&#xff08;CI&#xff09;工具&#xff0c;用于自动化构建、测试和部署软件项目。它提供了一个易于使用和可扩展的平台&#xff0c;帮助团队更高效地开发和交付软…...

初学51单片机之I2C总线与E2PROM

首先先推荐B站的I2C相关的视频I2C入门第一节-I2C的基本工作原理_哔哩哔哩_bilibili 看完视频估计就大概知道怎么操作I2C了&#xff0c;他的LCD1602讲的也很不错&#xff0c;把数据建立tsp和数据保持thd&#xff0c;比喻成拍照时候的摆pose和按快门两个过程&#xff0c;感觉还是…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

在rocky linux 9.5上在线安装 docker

前面是指南&#xff0c;后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章&#xff0c;二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑&#xff1a; &#x1f504; 一、起源与初创期&#xff1a;Swagger的诞生&#xff08;2010-2014&#xff09; 核心…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

Mysql8 忘记密码重置,以及问题解决

1.使用免密登录 找到配置MySQL文件&#xff0c;我的文件路径是/etc/mysql/my.cnf&#xff0c;有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用

一、方案背景​ 在现代生产与生活场景中&#xff0c;如工厂高危作业区、医院手术室、公共场景等&#xff0c;人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式&#xff0c;存在效率低、覆盖面不足、判断主观性强等问题&#xff0c;难以满足对人员打手机行为精…...

基于Java+VUE+MariaDB实现(Web)仿小米商城

仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意&#xff1a;运行前…...