Digamos que você tenha um método que executará como um thread:
def functionAsThread[F](function: => F, name: Option[String] = None) = {
val t = new Thread( new Runnable() { def run() { function } } )
t.start()
name.foreach(t.setName)
t
}
def periodicallyDoSomething(): Unit = {
while(keepDoingIt_?) {
doSomething()
Thread.sleep(frequency)
}
}
val somethingDoer = functionAsThread(periodicallyDoSomething, Some("SomethingDoer"))
Quando, inevitavelmente, o thread deve parar, como em um desligamento limpo, é claro que você dirá somethingDoer
para parar com somethingDoer.interrupt()
.
Como você deve lidar com o potencial InterruptedException
gerado Thread.sleep
?
Seu cérebro Scala deve levá-lo para embrulhar function
em um Try
e passar em um PartialFunction
para a recuperação, assim:
def functionAsThread[F](function: => F, name: Option[String] = None, recoverWith: PartialFunction[Throwable, Unit]) = {
val t = new Thread( new Runnable() { def run() { Try(function).recover(recoverWith) } } )
t.start()
name.foreach(t.setName)
t
}
Você ficará tão frustrado quanto eu quando isso não funcionar. A exceção ainda não foi capturada!
Analisando Try
um pouco mais, vemos por que isso não funciona:
object Try {
def apply[T](r: => T): Try[T] =
try Success(r) catch {
case NonFatal(e) => Failure(e)
}
}
Isso é interessante. Try
é apenas um que retorna um objeto ou conveniente . Mas que negócio é esse ?try{}catch{}
Success
Failure
NonFatal
object NonFatal {
def apply(t: Throwable): Boolean = t match {
// VirtualMachineError includes OutOfMemoryError and other fatal errors
case _: VirtualMachineError | _: ThreadDeath | _: InterruptedException | _: LinkageError | _: ControlThrowable => false
case _ => true
}
def unapply(t: Throwable): Option[Throwable] = if (apply(t)) Some(t) else None
Scala considera InterruptedException
ser uma exceção fatal. Ou seja, há uma decisão de design deliberada que um implementador deve usar para lidar com isso. Isso faz sentido porque um thread que está inativo deve ser informado explicitamente sobre o que fazer se for interrompido. Isso é apenas uma parte do uso .try{}catch{}
Thread.sleep
Portanto, uma maneira de corrigir o método é:
def periodicallyDoSomething(): Unit = {
try {
while(keepDoingIt_?) {
doSomething()
Thread.sleep(frequency)
}
} catch {
case e: InterruptedException => handleInterruption()
}
Pode ser melhor apenas experimentar , mas deixarei essa melhoria como um exercício para o leitor!Thread.sleep