我们公司的一位程序员同事对我说:“我遇到了一个非常奇怪的**!听他这么说有点紧张,我很好奇**什么能叫“怪异”。 然后他跟我解释,他写了一个for循环,然后用for循环的索引来得到数组对应下标的值,但是这个**好像没有效果,明明循环终止条件是设置i<=20,但最后发现i的值是21!我一听到他描述这样的事情,我立刻产生了兴趣。然后我拿他的**调试了一下,发现真的和他描述的一样,循环终止条件是i<=20,但是i的最后值是21!
*这个问题真的很奇怪,但我喜欢它!但是我看了一会儿他的***,有点不好意思,一时也不确定他的***写有没有问题!然而,唯一可以肯定的是,他在for循环中使用了异步线程,我想问题仍然出在线程上。
所以,我写了一个完全模拟他的写作风格的演示来测试我的想法。
我写了一个 0 到 20 的 for 循环,然后直接异步输出 i 的值,最后我发现这个 i 的值不仅输出 21,甚至 21 个输出输出 i 的一半以上都有 21 的值!
想了一会儿,我想出了问题的原因!
出现此问题的原因是循环使用异步输出,在并发输出的情况下,i 的输出顺序不确定,这也破坏了 i 值的原子性,最终可能导致 i 的值与输出时的预期不同。
打个简单的比方,因为for循环内部使用异步输出,所以for循环会在很短的时间内执行,而for循环执行完之后,就可能执行异步逻辑了,这时i的值可能是20,所以如果异步输出的逻辑是在for循环结束后执行的, 那么这个时候输出的 i,那么 i 的值应该是 20,所以,原则上会有大量的输出 20 个值!
但是,我们也发现 i 的值并不是我们想象的那样,它不是 20 而是 21,这就把我们带到了 for 循环的执行逻辑。
假设 i 的起始值为 0,结束值为 20,则 for 循环的执行逻辑是每次进行循环时都会给出 i+1,当它准备进入下一个循环时,会首先判断 i 是否大于结束值, 如果它大于它,则不会执行。
换句话说,当执行最后一个循环时,for 循环仍然给出 i+1,因此如果执行异步输出的逻辑,则 i 等于 21!
我们可以用一个简单的例子来证明这个推论(这里没有复杂的编程基础知识,都是关于验证的)。
我们可以在 for 循环之外定义一个值为 0 的变量 i,即避免在 for 循环时创建变量,让我们调用 for 循环之外的输出,当 for 循环结束时(这里没有异步操作),我们输出 i 的值,你会发现 i 的值是 21 而不是 20!
我们都知道这个问题,但是如果我的同事必须在他的 for 循环中使用异步怎么办?解决方法也很简单,就是直接定义一个变量来获取每个循环中 i 的值,并且这个变量相对于 for 循环是原子的,然后,在异步时间内使用这个变量而不是直接使用 i,就这样,问题就解决了!
如何?你学会了吗?