对于this answer我编写的代码如下:
def show_wait_spinner
dirty = false
spinner = Thread.new{
loop{
print "*"
dirty = true
sleep 0.1
print "\b"
dirty = false
}
}
yield
spinner.kill
print "\b" if dirty
end
print "A"
show_wait_spinner{ sleep rand }
puts "B"
目标是确保最终输出为"AB"
- 如果线程尚未打印,则打印最终"\b"
。在begin/rescue/ensure
存在的Ruby中,该代码对我来说似乎很混乱。所以我尝试了show_wait_spinner
的其他一些实现;所有这些都无法确保"AB"
始终是输出,而不是"A*B"
或"AB*"
。
是否有更简洁,更Ruby的方式来实现这种逻辑?
def show_wait_spinner
stop = false
stopm = Mutex.new
spinner = Thread.new{
loop{
print "*"
sleep 0.1
print "\b"
stopm.synchronize{ break if stop }
}
}
yield
stopm.synchronize{ stop = true }
STDOUT.flush
end
...但我的逻辑必须关闭,因为这总是导致“A * B”。
第二次尝试导致有时打印“A * B”,有时“AB”:
def show_wait_spinner
stop = false
spinner = Thread.new{
Thread.current[:stop] = false
loop{
print "*"
sleep 0.1
print "\b"
stopm.synchronize{ break if Thread.current[:stop] }
}
}
yield
spinner[:stop] = true
STDOUT.flush
end
def show_wait_spinner
spinner = Thread.new{
dirty = false
begin
loop{
print "*"
dirty = true
sleep 0.1
print "\b"
dirty = false
}
ensure
print "\b" if dirty
end
}
yield
spinner.kill
STDOUT.flush
end
def show_wait_spinner
spinner = Thread.new{
dirty = false
begin
loop{
print "*"
dirty = true
sleep 0.1
print "\b"
dirty = false
}
rescue
puts "YAY"
print "\b" if dirty
end
}
yield
spinner.raise
STDOUT.flush
end
答案 0 :(得分:4)
为什么不翻转导致它停在预定点的变量而不是杀死你的线程?如果让它循环并在循环结束时退出,那么你将不会遇到太多麻烦。
例如:
def show_wait_spinner
running = true
spinner = Thread.new do
while (running) do
print "*"
sleep 0.1
print "\b"
end
end
yield
running = false
spinner.join
end
print "A"
show_wait_spinner{ sleep rand }
puts "B"
当你调用Thread#kill
时,你不知道线程在哪里,线程没有机会清理它正在做的事情。如果不遵守礼貌的“停止运行”请求,您可以随时终止该线程。
答案 1 :(得分:3)
我更喜欢你的同步停止条件方法,但你有几个错误:
break
会突破块,而不是
循环。
def show_wait_spinner
stop = false
stopm = Mutex.new
spinner = Thread.new{
loop{
print "*"
sleep 0.1
print "\b"
break if stopm.synchronize{ stop }
}
}
yield
stopm.synchronize{ stop = true }
spinner.join
STDOUT.flush
end
print "A"
show_wait_spinner{ sleep rand }
puts "B"
我会避免任何涉及Thread#raise和Thread#kill的解决方案,因为他们的行为永远无法预测和纠正,请参阅Charles Nutter's rant about the brokenness of these methods。
Mutex#synchronize仅对于这个简单的布尔翻转是必要的,如果你真的非常关心在父线程设置var的情况下围绕竞争条件的精确定时,在这个例子中不太可能,所以你可以避免开销,只需设置并正常读取stop
。
答案 2 :(得分:2)
在您的互斥锁示例中,您需要在退出方法之前等待Thread完成。目前您将stop
设置为true,然后退出方法,打印B
并在微调器线程能够唤醒并打印最后一个退格键以删除*
字符之前结束脚本。
此外,break
中的stopm.synchronize{ break if stop }
仅退出内部块,而不是循环,因此您需要使用catch / throw等。
但是,您不需要互斥锁。这在1.9.3中适用于我:
def show_wait_spinner
exit = false
spinner = Thread.new{
loop{
print "*"
sleep 0.1
print "\b"
break if exit
}
}
yield
exit = true
spinner.join
end
在顶部添加$stdout.sync = true
使其在1.8.7中有效。