Scala 将迭代器映射到迭代器[Future]的行为不符合预期

Scala 将迭代器映射到迭代器[Future]的行为不符合预期,scala,Scala,使用Scala 2.13.0: implicit val ec=ExecutionContext.global val arr=(0到20)。ToInterator .map{x=> 睡眠(500); println(x); x } val fss=arr.map{slowX=> 未来{blocking{slowX}} } wait.result(Future.sequence(fss),Inf) 问题 arr是一个迭代器,其中每个项目需要500毫秒的处理时间。我们将迭代器映射为Future{

使用Scala 2.13.0:

implicit val ec=ExecutionContext.global
val arr=(0到20)。ToInterator
.map{x=>
睡眠(500);
println(x);
x
}
val fss=arr.map{slowX=>
未来{blocking{slowX}}
}
wait.result(Future.sequence(fss),Inf)
问题
arr
是一个迭代器,其中每个项目需要500毫秒的处理时间。我们将迭代器映射为
Future{blocking{…}
,目的是使处理并行(使用全局执行上下文)。最后我们运行
Future.sequence
使用迭代器

给定
Future.apply[T](body:=>T)
blocking[T](body:=>T)
的定义,
body
被延迟传递,这意味着
body
将在将来处理。如果我们将其注入到
Iterator.map
的定义中,我们会得到
def next()=Future{blocking(self.next())}
,因此迭代器的每个项都应该在将来处理

但是,当我尝试这个示例时,我可以看到迭代器是按顺序使用的,这不是预期的


那是Scala bug吗??还是我遗漏了什么?

不,这不是错误,因为:

val arr = (0 until 20).toIterator
// this map invokes first and executed sequentially, because it executes in same thread.
      .map { x => 
        Thread.sleep(500);
        println(x);
        x
      }

    // This go sequentially because upstream map executed sequentially in same thread.
    // So, "Future { blocking { slowX } }" can be replaced with "Future.successfull(slowX)"
   // because no computation executed
    val fss = arr.map { slowX =>
      Future { blocking { slowX } }
    }
如果希望完全异步执行,可以执行以下操作:

def heavyCalculation(x: Int) = {
  Thread.sleep(500);
  println(x);
  x
}

val result = Future.traverse((0 until 20).toList) { x => 
  Future(blocking(heavyCalculation(x)))
}

Await.result(result, 1 minute)

正在工作的Scatie示例:

不,这不是一个bug,因为:

val arr = (0 until 20).toIterator
// this map invokes first and executed sequentially, because it executes in same thread.
      .map { x => 
        Thread.sleep(500);
        println(x);
        x
      }

    // This go sequentially because upstream map executed sequentially in same thread.
    // So, "Future { blocking { slowX } }" can be replaced with "Future.successfull(slowX)"
   // because no computation executed
    val fss = arr.map { slowX =>
      Future { blocking { slowX } }
    }
如果希望完全异步执行,可以执行以下操作:

def heavyCalculation(x: Int) = {
  Thread.sleep(500);
  println(x);
  x
}

val result = Future.traverse((0 until 20).toList) { x => 
  Future(blocking(heavyCalculation(x)))
}

Await.result(result, 1 minute)

工作示例:

首先,这不是一个合适的基准测试,您实际上还没有正式证明这是顺序的,而不是并行的(尽管从源代码中“很明显”它不是)

其次,期货的迭代器可能是个坏主意;在这一点上,研究像Akka Streams、fs2MonixZIO这样的流媒体解决方案可能是有意义的

第三,甚至有一堆<代码>阻止<代码>期货的意义是什么?你实际上并没有赢太多

第四,问题是第二个
映射
没有通过第一个
映射
的块,只是结果。因此,在创建
未来之前,您实际上进行了
睡眠

第五,你可能想这样做

val result=Future.transverse(数据){elem=>
未来{
阻塞{
//在这里处理元素。
}
}
}
等待结果(结果,Inf)

首先,这不是一个合适的基准测试,实际上您还没有正式证明这是顺序的,而不是并行的(尽管从源代码中“很明显”它不是)

其次,期货的迭代器可能是个坏主意;在这一点上,研究像Akka Streams、fs2MonixZIO这样的流媒体解决方案可能是有意义的

第三,甚至有一堆<代码>阻止<代码>期货的意义是什么?你实际上并没有赢太多

第四,问题是第二个
映射
没有通过第一个
映射
的块,只是结果。因此,在创建
未来之前,您实际上进行了
睡眠

第五,你可能想这样做

val result=Future.transverse(数据){elem=>
未来{
阻塞{
//在这里处理元素。
}
}
}
等待结果(结果,Inf)

其他答案指向正确的方向,但正式答案如下:
迭代器.map(f:A=>B)
的签名告诉我们
A
在应用
f
之前计算
A
(因为它不是
=>A
)。因此,在主线程中计算
next()

其他答案指向正确的方向,但正式答案如下:
Iterator.map(f:A=>B)
的签名告诉我们
A
是在应用
f
之前计算的(因为它不是
=>A
)。因此,
next()
在主线程中进行计算。

arr.map
在传递到
未来之前,正在计算上一个
map
中的值,因此
睡眠
发生在
未来
arr.map
在传递到
F之前,正在计算上一个
map
中的值未来
,因此
睡眠
发生在
未来之外
阻塞
被精确地使用,这样全局执行上下文就不会阻塞;)请看这里(全局执行上下文)@stackoverflow这是我的观点,您只是创建了一组线程来阻塞它们。重点是什么?没有阻塞主线程?您正在
wait
中阻塞它,等待所有阻塞完成。异步启动所有阻塞操作?然后你必须小心,因为你可能会创建太多的线程,从而耗尽你的内存。@StackOverflow同样,只有迭代器能够并行地生成多个元素时,这才有效,而迭代器可能不能,所以最后,由于线程的开销,它将比串行执行慢。最后,即使可以,您也不应该使用可变性+并发,这只会导致难以调试的错误。使用适当的并发抽象(如流)。
阻塞
被精确地使用,这样全局执行上下文就不会阻塞;)请看这里(全局执行上下文)@stackoverflow这是我的观点,您只是创建了一堆线程来阻止它们。重点是什么?没有阻塞主线程?您正在
wait
中对其进行阻塞,以等待所有阻塞被取消