Jenkins并行步骤迭代变量异常:为何所有任务均取末次循环值?
Jenkins并行步骤迭代变量取值异常问题排查与修正
异常产生原因
这个问题是Groovy作用域特性与Jenkins并行任务执行时机共同作用的经典问题:
- 代码里的for循环是同步快速执行完成的,循环过程中往
tasksMap中存入的所有闭包(代码块)不会立刻执行,只是把闭包的定义保存到了Map结构中 - 代码中定义的迭代变量
j是全局共享的作用域变量,所有闭包内部引用的j,本质都是指向这同一个全局变量的内存引用 - 整个for循环执行完成时,全局的
j已经走到了循环终止的末次值:循环判断边界为j <= registries.size(),由于registries长度为4,当j自增到5时,5不满足循环判断条件,循环退出,此时全局j的值固定为5 - 后续执行
parallel(tasks)时,所有存入Map的闭包才会被真正触发执行,此时所有闭包读取到的共享全局j的值都是末次的5,最终只会生成一个名为5的文件,完全达不到预期生成0-5共6个文件的效果。
修正方案
核心调整思路是让每个并行闭包绑定属于自己迭代轮次的独立变量值,不要共用全局的共享迭代变量,常用的可落地实现有两种:
循环内定义局部变量拷贝当前迭代值
每轮循环执行时,在当前循环的局部块作用域内新建一个变量,把当前轮的j值赋值给它,让每个闭包引用这个专属的局部变量,而非全局的j:
registries = [1,2,3,4] Map tasks = [:] for (j=0; j<=registries.size(); j++) { // 每轮循环生成独立的局部变量,存储当前迭代的j值 def currentIterValue = j tasks["Code ${j}"] = { -> sh "touch ${currentIterValue}" } } parallel(tasks) sh "ls -lrt"
这种方式生效的逻辑是:每轮循环的局部变量属于独立的块作用域,闭包在创建时会绑定自己生成那一轮的局部变量引用,后续循环对全局j的修改不会影响已经被闭包绑定的局部值。
改用集合each遍历替代手写for循环
Groovy为集合提供的each遍历方法,每轮传入的迭代参数本身就是独立的局部变量,不存在全局共享的问题,不需要额外拷贝变量也能拿到正确的迭代值:
registries = [1,2,3,4] Map tasks = [:] // 生成0到registries.size的连续索引范围,直接遍历 (0..registries.size()).each { j -> // 此处的j是each传递的当前轮独立局部变量 tasks["Code ${j}"] = { -> sh "touch ${j}" } } parallel(tasks) sh "ls -lrt"
内容的提问来源于stack exchange,提问作者thatguy
相关产品推荐
相关产品推荐

