3

假设我正在阅读一个InputStream.

我通常会怎么做:

val inputStream = ...
try {
    doStuff(inputStream)
} finally {
    inputStream.close()
}

无论是否doStuff抛出异常,我们都会关闭InputStream.


我将如何使用迭代器:

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 / 01 / 1,您会看到流关闭。

当然,我可以保持对原始流的引用并在失败时关闭它,但 AFAIK 使用 iteratees 的想法是创建可组合的迭代而不必这样做。


  1. 这是预期的行为吗?

  2. 有没有办法使用迭代器,以便始终正确处理资源?

4

1 回答 1

2

迭代器是专门为安全资源管理而设计的。见Iteratee IO的第一句话:安全、实用、声明式的输入处理:

Iteratee IO 是一种具有精确资源控制的增量输入处理方式。

这个想法是,当您的资源仅通过迭代器访问时,拥有资源的代码可以准确地判断迭代器何时完成资源并立即关闭它。另一方面,当手动管理迭代时(如传统InputStream的),资源的用户负责关闭它。这可能导致泄漏。

话虽如此,Play 2.1 中存在一个错误,fromStream无法管理其底层证券的关闭InputStream!此错误已在 Play 2.2 中修复。

您可以通过在迭代完成时关闭资源来查看fromStream代码以了解如何Enumerator修复。onDoneEnumerating

于 2014-07-29T00:01:40.187 回答