如何解决Scala Future.sequence(Iterator) 抛出 NoSuchElementException
为什么下面的代码会抛出java.util.NoSuchElementException: next on empty iterator
???
implicit val ec = ExecutionContext.global
val it = (0 until 10).toIterator.map{
x =>
Thread.sleep(500)
println(x)
x
}
val it2 = new Iterator[Future[Int]] {
def hasNext = it.hasNext
def next() = Future { blocking { it.next() } }
} //This should be equivalent to it.map{x => Future{blocking{x}}}
Await.result(Future.sequence(it2),Inf)
辅助问题:为什么 it2
的行为不像 it.map{x => Future{blocking{x}}}
??
解决方法
您有一个并发错误,有人调用 hasNext
它返回 true
因为底层迭代器能够生成新元素。然后它调用 next
这将返回一个未来,完成后将保存 next
值。
但是,对于调用者 (在本例中为 Future.sequence
) 它已经完成,因此它再次调用 hasNext
将再次返回 true
但因为底层迭代器没有t 完成了第一个元素的生成,所以我们生成了第二个元素,直到第一个元素完成(这样你就不会赢得未来)。
这会继续创建越来越多的期货,最终将调用 next
并获得 NoSuchElementException
,因为底层迭代器没有更多元素,但您已经创建了越来越多的期货等待越来越多的元素。
所以,换句话说,你在 true
中得到一个 hasNext
只是因为它没有完成生成元素,因此没有改变它的内部状态;不是因为它实际上有更多元素。
这是一个很好的记录,可变性 + 并发性 = 头痛。
同样,我建议您为此使用适当的抽象
另一个答案指出了正确的方向,但我只是改写得更干净一点。通过在 next()
和 hasNext
方法中添加一些打印输出,可以看到它们被连续调用。这是因为 it
和 it2
不同步:it.next()
是阻塞的,而 it2.next()
不是,而是 it2.hasNext == it.hasNext
,所以 {{1 }} 正在阻塞。结果 it2.hasNext
的 it2
和 next()
不同步,这是一团糟!
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。