• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

SOC/IP验证工程师

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

for循环和fork...join_none联合挖的坑


示例中,在for循环中使用了fork...join_none结构调用$write显示每次循环执行过程中循环变量的变化值,但是循环结束后我们发现显示的循环变量的值都是一样,并没有记录循环变量的每次变化情况,这是为什么呢?下面我们通过示例说明在for循环中使用fork...join_none是多么糟心的一件事。
针对上面的示例,首先我们需要了解下for循环中循环变量是如何变化的。在for循环中,循环变量的调整值是当前次循环结束时自动按照for循环中描述的调整方式进行变更,并不是在for结构开始就进行变化的。
【示例】

【仿真结果】

示例中,第三次循环结束时,循环变量递增为3,然后程序再次准备执行第四次循环,但是此时循环变量的值为3,不满足循环条件,所以第四次循环不会进行。可见循环变量如果没有在循环体内部额外进行处理的话,那么在当前次循环执行完后循环变量会自动按照循环变量调整表达式进行调整。在了解了for循环循环变量自动变化的这个特点之后,我们再对fork...join_none的执行特点进行示例说明。
【示例】

【仿真结果】

示例中,fork...join_none执行时,首先针对其中的语句创建三个线程,但是创建后并不会马上执行,而是触发了fork...join_none之后语句后才会执行已经创建的三个线程,所以从仿真结果中我们可以看到,首先显示了fork...join_none之后的显示语句,然后才显示了fork...join_none之中的三条显示语句。可见fork...join_none中创建的线程并不是立刻执行,而是只有触发了其后的线程之后才会被执行。
在了解了for循环和fork...join_none结构的上述特点之后,我们再回到开始的示例中,在for结构中使用fork...join_none结构的情况分析。
【示例】

【仿真结果】

示例中,for循环每执行一次就会通过fork...join_none创建一个线程,但是这些创建的线程并不会创建了就立刻执行,而是在fork...join_none之后语句被触发后才一并执行。因为这些线程使用的都是同一个变量i,即for循环的循环变量,所以当3次循环结束时,还未执行创建的进程时,循环变量已经变成了3(for循环最后一次循环执行完后,循环变量自动递增),之后执行这些创建的线程,这些显示语句显示的结果是一样的。为了让创建的线程能够获得不一样的循环变量的值,可以在线程中额外的声明一个动态变量,如下例。
【示例】
示例中,for循环每执行一次就会通过fork...join_none创建一个线程,但是这些创建的线程并不会创建了就立刻执行,而是在fork...join_none之后语句被触发后才一并执行。因为这些线程使用的都是同一个变量i,即for循环的循环变量,所以当3次循环结束时,还未执行创建的进程时,循环变量已经变成了3(for循环最后一次循环执行完后,循环变量自动递增),之后执行这些创建的线程,这些显示语句显示的结果是一样的。为了让创建的线程能够获得不一样的循环变量的值,可以在线程中额外的声明一个动态变量,如下例。
【示例】

【仿真结果】

示例中,在fork...join_none中定义了一个动态变量j,这样每执行一次for循环,fork...join_none创建一次对应的线程,并且因为每次创建的线程中j都是动态的,即每个线程中的j都属于各线程本身,且互相不影响,所以在每次创建对应线程时,当前次创建的线程中的动态变量j都会保存当前次循环变量i的值。在循环创建完所有对应线程后,执行fork...join_none之后的语句,然后再一次性将fork...join_none创建的所有线程一并执行,此时仿真打印的j值是不一样,并没有受到i的最终值的影响。
在示例中,动态变量在声明的同时进行了赋值操作,因为声明性语句在其所在的过程块中将会最先执行,所以此时对于该变量的赋值操作在进行声明时也一并完成。那么可不可以将动态变量的声明与赋值操作分离开来呢,我们看下面的示例。
【示例】


此例结构同上例,唯一不同的在于j声明的时候并没有给j赋初值,所以在每次创建线程时j并没有被赋值。而j被赋值为i的操作实际上算为fork...join_none创建的一个线程,所以j获得的i实际上是fork...join_none已经完成所有线程的创建了。在fork...join_none后语句执行后,依次启动已经创建的线程,在这些线程执行的时候,循环变量i的值已经是3了,所以每个启动的线程在执行时,进行对j赋值时,j获得的是循环变量最后的值,即所有3个线程获得的i都是一个值。
示例中是将变量j声明为了动态变量,那么此时可不可以将j声明为静态变量呢?我们看下面的示例。
【示例】

【仿真结果】

编译错误!示例中,静态变量在声明的同时进行了初始化,此操作应该在仿真开始就执行(静态变量的初始化在仿真开始时进行),而仿真开始时,实际上for循环并没有执行,即此时i还不存在,所以此时编译错误!那么如果将声明和初始化分开是否合适呢?这时仿真结果与将automatic变量j的声明和初始化分开效果一样,即每个线程获得的i的值相同,都是i最后一次循环执行完后的递增值3.
在了解了上述for和fork...join_none结构仿真时的各种情况之后,有些朋友可能不期望将循环变量的调整交由for循环自动完成,而是在循环体内部手动的对循环变量进行修改,那么这样的仿真会导致什么情况呢?请看下例。
【示例】

【仿真结果】
DEAD LOOP!
并且在仿真的时候会提示如下:

仿真进入死循环,为什么会这样呢?这是因为for循环体中的fork...join_none中创建的线程会在fork...join_none后的语句执行后才会启动,而此时循环本身并没有实现循环变量的自动调整(其自动调整的操作已经被包含在了要启动的线程中,所以此时for循环的循环变量将一直保持0,即一直满足循环执行的条件,进入了死循环),导致了for循环后的语句也不会执行。
通过上面的示例,在使用for循环调用fork...join_none产生多个线程时需要注意以下几点可以有效避免两者混用产生的各种坑:
Ø for循环进行循环时,其循环变量是在当前次循环结束后才会按照调整表达式变化,当然for循环变量的调整表达式可以出现在循环体内部从而实现循环变量的手动调整;
Ø fork...join_none开启的线程在创建后并不会立刻执行,而是在触发了fork...join_none后的语句之后才会一并启动;
Ø 变量的声明会在其所在语句块中最先执行;
Ø 动态变量仅存在于其所在线程中,多个线程中的动态变量互相不产生影响;

posted on 2023-05-25 21:48  SOC验证工程师  阅读(921)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3