我的问题可能很模糊(无法考虑如何很好地描述它),但希望这个例子可以使事情变得更清楚:
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在此之前不要理会它。抱歉,如果已经问到我找不到示例。
答案 0 :(得分:2)
问题在于,代码正在从两个线程读取var
,而没有警告编译器将要执行此操作,这导致了不可预测的行为。编译器不知道a
的值是否会改变,因此完全可以将该值缓存在寄存器或其他内存位中。如果确实如此,那么while
循环将永远旋转。
碰巧您的第一个测试失败了,第二个测试成功了,但这是由于您使用的特定编译器和调度程序导致的,并且在不同的系统上可能会有所不同。
解决方案是避免使用共享变量并使用适当的同步机制。在这种情况下,Promise
可能会成功。
答案 1 :(得分:0)
a
必须为@volatile
,如果没有其他线程的写操作,则不能保证它对当前线程不可见,直到它遇到“内存障碍”(代码中的特殊点,如注释中指出的,从概念上讲,所有高速缓存都被刷新了,而不必直接映射到特定cpu的硬件处理方式。这就是第二种情况起作用的原因-println
调用中有很多内存障碍。
因此,将var a ...
更改为@volatile var a ...
可以使其正常工作,但是,严重的是,请勿使用vars
。至少直到您学到足够多的Scala才能识别出必须拥有的情况。