假设我正在读取InputStream
。
我通常怎么做:
val inputStream = ...
try {
doStuff(inputStream)
} finally {
inputStream.close()
}
无论doStuff
是否抛出异常,我们都将关闭InputStream
。
如何使用iteratees:
val inputStream ...
Enumerator.fromStream(inputStream)(Iteratee.foreach(doStuff))
InputStream
将被关闭(即使doStuff
抛出异常)?
一个小测试:
val inputStream = new InputStream() { // returns 10, 9, ... 0, -1
private var i = 10
def read() = {
i = math.max(0, i) - 1
i
}
override def close() = println("closed") // looking for this
}
Enumerator.fromStream(inputStream)(Iteratee.foreach(a => 1 / 0)).onComplete(println)
我们只看到:
Failure(java.lang.ArithmeticException: / by zero)
流从未关闭。将1 / 0
替换为1 / 1
,您将看到流关闭。
当然,我可以维护对原始流的引用,并在失败的情况下关闭它,但据我所知,使用iteratees的想法是创建可组合迭代,而不必这样做。
这是预期行为吗?
是否有一种方法来使用迭代,使资源总是正确处置?
Iteratees是专门为安全的资源管理而设计的。参见Iteratee IO的第一句话:安全、实用、声明性的输入处理:
Iteratee IO是一种具有精确资源控制的增量输入处理方式。
的想法是,当你的资源只能通过迭代器访问时,拥有资源的代码可以准确地告诉迭代器何时完成对资源的使用,并立即关闭它。另一方面,当迭代是手动管理的(如传统的InputStream
),资源的用户负责关闭它。这可能导致泄漏。
说,有一个bug在Play 2.1 fromStream
没有管理关闭其底层InputStream
!此错误已在Play 2.2中修复。
您可以查看fromStream
代码,以了解Enumerator
是如何通过使用onDoneEnumerating
在被迭代者完成时关闭资源来修复的。