Future 中的空 while 循环导致 Future 在 Scala 中永远不会 return
Empty while loop in Future causes Future to never return in Scala
我的问题可能很模糊(想不出如何描述它)但希望这个例子能让事情更清楚:
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)
}
}
}
在第一个测试 (Fails for some reason
) 中,while 循环有一个空循环体。在第二个测试 (Passes...why?!?
) 中,while 循环体中有一个 println 语句。我最初的想法是垃圾收集正在做一些时髦的事情,但是对于那个 whenReady
声明,我期待 return 的东西,所以我希望 GC 在那之前不要管它。很抱歉,如果有人问过我找不到示例。
问题是代码正在从两个线程读取 var
而没有警告编译器它将要执行此操作,这会导致不可预测的行为。编译器不知道 a
的值将在其脚下发生变化,因此完全允许将该值缓存在寄存器或其他内存中。如果是这样,while
循环将永远旋转。
碰巧您的第一个测试失败而第二个测试成功,但这是您使用的特定编译器和调度程序的结果,并且在不同的系统上可能会有所不同。
解决方案是避免使用共享变量并使用适当的同步机制。在这种情况下,Promise
可能会成功。
a
需要是 @volatile
,没有它,从其他线程写入不能保证对当前线程可见,直到它命中 "memory barrier"(一个特殊点在代码,其中所有缓存都被刷新 - 正如评论中指出的 概念意义上的 ,不一定直接映射到特定 cpu 硬件如何处理它)。这就是第二种情况起作用的原因 - println
调用中有很多内存障碍。
因此,将 var a ...
更改为 @volatile var a ...
将使其工作...但是,严重的是,不要使用 vars
。至少,直到你学会了足够多的 scala 才能识别你必须拥有它们的情况。
我的问题可能很模糊(想不出如何描述它)但希望这个例子能让事情更清楚:
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)
}
}
}
在第一个测试 (Fails for some reason
) 中,while 循环有一个空循环体。在第二个测试 (Passes...why?!?
) 中,while 循环体中有一个 println 语句。我最初的想法是垃圾收集正在做一些时髦的事情,但是对于那个 whenReady
声明,我期待 return 的东西,所以我希望 GC 在那之前不要管它。很抱歉,如果有人问过我找不到示例。
问题是代码正在从两个线程读取 var
而没有警告编译器它将要执行此操作,这会导致不可预测的行为。编译器不知道 a
的值将在其脚下发生变化,因此完全允许将该值缓存在寄存器或其他内存中。如果是这样,while
循环将永远旋转。
碰巧您的第一个测试失败而第二个测试成功,但这是您使用的特定编译器和调度程序的结果,并且在不同的系统上可能会有所不同。
解决方案是避免使用共享变量并使用适当的同步机制。在这种情况下,Promise
可能会成功。
a
需要是 @volatile
,没有它,从其他线程写入不能保证对当前线程可见,直到它命中 "memory barrier"(一个特殊点在代码,其中所有缓存都被刷新 - 正如评论中指出的 概念意义上的 ,不一定直接映射到特定 cpu 硬件如何处理它)。这就是第二种情况起作用的原因 - println
调用中有很多内存障碍。
因此,将 var a ...
更改为 @volatile var a ...
将使其工作...但是,严重的是,不要使用 vars
。至少,直到你学会了足够多的 scala 才能识别你必须拥有它们的情况。