我有一个Java库,可以执行长时间运行的阻塞操作。该库旨在响应用户取消请求。库集成点是Callable接口。
我需要在Actor中将这个库集成到我的应用程序中。我最初的想法是做这样的事情:
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
val callable: java.util.concurrent.Callable[Void] = ???
val fut = Future {
callable.call()
}
fut.onSuccess {
case _ => // continue on success path
}
fut.onFailure {
case throwable => // handle exceptions
}
我认为这段代码可以正常运行,因为它不会阻止actor。但我不知道如何提供取消操作的方法。假设在callable正在处理时,actor接收一条消息,指示它应该取消在callable中正在处理的操作,并且该库通过中断处理线程来响应取消请求。
从Actor中提交Callable并在稍后取消操作的最佳做法是什么?
更新
要清楚,该库公开了java.util.concurrent.Callable接口的实例。可调用本身并不提供取消方法。但是可调用对象以这样的方式实现,即它响应于由于中断线程而取消。在java中,这可以通过将callable提交给Executor来完成。这将返回java.util.concurrent.Future。正是这个Future对象提供了cancel方法。
在Java中我会做以下事情:
ExecutorService executor = ...
Callable c = ...
Future f = executor.submit(c);
...
// do more stuff
...
// If I need to cancel the callable task I just do this:
f.cancel(true);
似乎java.util.concurrent.Future和scala.concurrent.Future之间存在脱节。 java版本提供了取消方法,而scala版本没有。
在Scala我会这样做:
// When the Akka Actor receives a message to process a
// long running/blocking task I would schedule it to run
// on a different thread like this:
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
val callable: java.util.concurrent.Callable[Void] = ???
val fut = Future {
callable.call()
}
fut.onSuccess {
case _ => // continue on success path
}
fut.onFailure {
case throwable => // handle exceptions
}
// But now, if/when the actor receives a message to cancel
// the task because it is taking too long to finish (even
// though it is running in the background) there is no way
// that I know of to cancel or interrupt the
// scala.concurrent.Future.
是否有用于取消scala.concurrent.Future的惯用scala方法?
答案 0 :(得分:2)
根据我的理解,您的库正在展示具有call
和一些cancel
方法的界面,对吧?我假设你可以随时打电话取消。像下面这样的例子可以让你开始。
class InterruptableThingy extends Actor {
implicit val ctx = context.system.dispatchers.lookup("dedicated-dispatcher")
var counter = 0
var tasks = Map.empty[Int, HasCancelMethod]
def receive = {
case "doThing" =>
counter += 1
val id = counter
val thing = ???
Future { thing.call() } onSuccess {} // ...
tasks(id) = thing
sender() ! id
case Interrupt(id) =>
tasks(id).cancel()
tasks -= id
}
}
case class Interrupt(taskId: Int)
请注意,我们正在使用专门的调度程序来阻止期货。这是一个非常好的模式,因为您可以将专用调度程序配置为适合您的阻塞工作负载(并且不会在默认调度程序中耗尽资源)。调度员在此处的文档中有更详细的解释:http://doc.akka.io/docs/akka/2.3.3/scala/dispatchers.html