for循环+fork-join_none的隐蔽漏洞

devtools/2024/11/15 0:38:09/

1. 翻车现场

       比如某个场景中我们希望用for循环和fork嵌套开辟循环的线程,打印出10个选美者的编号和等级,代码很可能是这样实现的:

for (int i=0 ; i<10; i++)  beginfork$display(“No%0d,My face_grade is %0d”, i ,i );join_none
end

         然而实际的结果是什么呢?

No10, My face_grade is 10
No10, My face_grade is 10
No10, My face_grade is 10
No10, My face_grade is 10
No10, My face_grade is 10
No10, My face_grade is 10
No10, My face_grade is 10
No10, My face_grade is 10
No10, My face_grade is 10
No10, My face_grade is 10

        明明是0-9的循环,却打印出了10个10的结果

2. for循环原理

        看下这段代码:

int apple_num;
for (apple_num=0 ; apple_num<10; apple_num++);       $display(“i have %0d apples”, apple_num );

        这个代码打印出来的是:i have 10 apples

        这个代码,for循环是执行的一个空语句,for结束后才进行打印循环因子。让不注重细节的伙伴们再认识下for,for在最后执行完成他的值是还要再走apple_num++的,正是因为加到了10,才不满足apple_num<10的条件不再进行循环下去了。

        那么再回过头看刚刚的代码,我们知道了10是for循环执行玩了之后循环因子i的值。也就是说:for循环的时候依次创建了10个进程,然后等for循环结束后,才并行执行10个fork进程。因为fork-join_none,for全部循环完了以后, 10个$display(“No%0d,My face_grade is %0d”, i ,i ); 才并行的执行完!!在打印的时候得到的i值就是最后的10了。换句话理解:这10个并行的$display里面的i其实是同一个int i,i++是会改变它的。

3. 解决办法

        可以通过automatic变量来解决这个问题:

for (int i=0 ; i<10; i++)  beginfork   begin   automatic int j=i;       $display(“No%0d,My face_grade is %0d”, j ,j ); endjoin_none
end

        它的输出这是我们期望的:

No1, My face_grade is 1
No2, My face_grade is 2
No3, My face_grade is 3
No4, My face_grade is 4
No5, My face_grade is 5
No6, My face_grade is 6
No7, My face_grade is 7
No8, My face_grade is 8
No9, My face_grade is 9
No10, My face_grade is 10

        是什么原理呢?我们加了一个automatic int j=i,把i给j,我们打印j。此处automatic类型,意味着进入fork进程被创建,结束被撤销。保证了10个并行的display语句,每个进程中的j是它自己的,是独一无二的。

        那么只要有这个automatic就一定能实现吗?其实也不是,我们可以把这段代码理解为两个过程:“创建进程”、“执行进程”。创建进程的时候会创建10个并行的进程,然后统一执行。

        这句神奇的automatic int j=i;偏偏就是在创建进程的过程中就执行了,假如我们加入了#0的延时或是先声明automatic j然后赋值j=i,输出也依然会全是10!

for (int i=0 ; i<10; i++)  beginfork #0;   begin   automatic int j=i;       $display(“No%0d,My face_grade is %0d”, j ,j ); endjoin_none
end

        看来最关键的问题还在于执行顺序,那么我们只需要记住最简单的处理方式就是:用一个automatic int j=i 转一下,一定要在fork的一开始定义,并且赋值。

另:SystemVerilog书中第7章的自动变量中也探讨了相同的问题,可以学习参考。


http://www.ppmy.cn/devtools/110922.html

相关文章

【苍穹外卖】前端 Day 1

1 Vue 1.1 通过 vue cli 脚手架创建前端工程 1.2 项目结构 1.3 启动项目 VS Code 启动前端项目&#xff1a; npm run serve 注意这里占用端口号 8080 与 java springboot 占用端口号一致&#xff0c;有冲突 serve 是这个名字 终止&#xff1a;ctrl c 修改端口号 2 vue 基本…

MyBatis-Plus 与 Mockito:解决 Lambda 缓存初始化问题

问题背景 MyBatis-Plus 提供了便捷的 Lambda 查询表达式&#xff0c;但它依赖于实体类与数据库表的映射缓存。如果在测试环境中&#xff0c;这些映射未正确初始化&#xff0c;可能导致 can not find lambda cache for this entity 异常。这一问题特别容易在与 Mockito 搭配使用…

Chai-1:药物分子结构预测的新型多模态基础模型

参考: https://www.chaidiscovery.com/blog/introducing-chai-1 https://github.com/chaidiscovery/chai-lab https://chaiassets.com/chai-1/paper/technical_report_v1.pdf 这是一种用于分子结构预测的新型多模态基础模型,可在与药物发现相关的各种任务中执行最先进的功能。…

安装Anaconda(过程)

Anaconda是一个开源的Python发行版本&#xff0c;用来管理Python相关的包&#xff0c;安装Anaconda可以很方便的切换不同的环境&#xff0c;使用不同的深度学习框架开发项目&#xff0c;本文将详细介绍Anaconda的安装。 一、安装 1、安装方式 官网&#xff1a;“https://www.…

编译原理/软件工程核心概念-问题理解

目录 1.程序的编译执行过程 2.指针和引用的区别 3.堆和栈的区别 4.最熟悉的编程语言- Python&#xff1a;介绍PyTorch和TensorFlow框架 5.C与C的区别 6.软件工程是什么&#xff1f; 7.简述瀑布模型 8.敏捷开发方法是什么&#xff1f;它与瀑布模型相比有哪些优势和劣势 1…

idear导入他人项目如何快速运行

最近idear经常导入别人的项目&#xff0c;结果永远在加载依赖项。网上查了一堆资料&#xff0c;什么jdk问题&#xff0c;环境变量问题&#xff0c;maven仓库路径问题&#xff0c;总之就是没啥用。那有没有什么简单粗暴的办法&#xff0c;能够导入项目后快速运行呢。 解决方法&a…

C++ 类型转换

前言 在C中&#xff0c;类型转换是指将一个变量或表达式从一种数据类型转换为另一种数据类型的过程。类型转换有两种基本形式&#xff1a;隐式类型转换和显式类型转换。 隐式转换 这是编译器自动进行的类型转换&#xff0c;通常发生在不同类型的变量进行运算时。例如&#x…

Angular-Cli脚手架介绍、安装并搭建项目

创建一个项目# 在创建项目之前&#xff0c;请确保 angular/cli 已被成功安装。 执行以下命令&#xff0c;angular/cli 会在当前目录下新建一个名称为 PROJECT-NAME 的文件夹&#xff0c;并自动安装好相应依赖。 $ ng new PROJECT-NAME 注意: 有可能会报错类似下面这种 The A…