Future中的while循环为空会导致Future永远不会在Scala中返回

时间:2018-07-10 15:27:10

标签: scala while-loop future scalatest

我的问题可能很模糊(无法考虑如何很好地描述它),但希望这个例子可以使事情变得更清楚:

featureB

在第一个测试(class IntTestFake extends FunSpec with ScalaFutures { describe("This"){ it("Fails for some reason"){ var a = "Chicken" val b = "Steak" def timeout() = Future{ while(a != b){} } Future{ Thread.sleep(3000) a = b } whenReady(timeout(), Timeout(20 seconds), Interval(50 milliseconds))(result => result) } it("Passes...why?!?"){ var a = "Chicken" val b = "Steak" def timeout() = Future{ while(a != b){ println("this works...") } } Future{ Thread.sleep(3000) a = b } whenReady(timeout(), Timeout(20 seconds), Interval(50 milliseconds))(result => result) } } } )中,while循环的主体为空。在第二个测试(Fails for some reason)中,while循环主体中有一个println语句。我最初的想法是垃圾回收正在做一些时髦的事情,但是使用Passes...why?!?语句,我希望返回一些东西,因此我希望GC在此之前不要理会它。抱歉,如果已经问到我找不到示例。

2 个答案:

答案 0 :(得分:2)

问题在于,代码正在从两个线程读取var,而没有警告编译器将要执行此操作,这导致了不可预测的行为。编译器不知道a的值是否会改变,因此完全可以将该值缓存在寄存器或其他内存位中。如果确实如此,那么while循环将永远旋转。

碰巧您的第一个测试失败了,第二个测试成功了,但这是由于您使用的特定编译器和调度程序导致的,并且在不同的系统上可能会有所不同。

解决方案是避免使用共享变量并使用适当的同步机制。在这种情况下,Promise可能会成功。

答案 1 :(得分:0)

a必须为@volatile,如果没有其他线程的写操作,则不能保证它对当前线程不可见,直到它遇到“内存障碍”(代码中的特殊点,如注释中指出的,从概念上讲,所有高速缓存都被刷新了,而不必直接映射到特定cpu的硬件处理方式。这就是第二种情况起作用的原因-println调用中有很多内存障碍。

因此,将var a ...更改为@volatile var a ...可以使其正常工作,但是,严重的是,请勿使用vars。至少直到您学到足够多的Scala才能识别出必须拥有的情况。