Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/security/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading Scala:加入/等待不断增长的未来队列_Multithreading_Scala_Future_Scala.js - Fatal编程技术网

Multithreading Scala:加入/等待不断增长的未来队列

Multithreading Scala:加入/等待不断增长的未来队列,multithreading,scala,future,scala.js,Multithreading,Scala,Future,Scala.js,我启动了几个异步进程,如果需要的话,这些进程可以启动更多的进程(想想遍历目录结构之类的东西)。每个进程都会返回一些东西,最后我希望等待所有进程的完成,并安排一个函数来处理结果集合 天真的尝试 我的解决方案尝试使用可变的ListBuffer(我一直在向其中添加我生成的未来)和Future.sequence来安排一些函数在完成此缓冲区中列出的所有这些未来时运行 我准备了一个简单的例子来说明这个问题: object FuturesTest extends App { var queue = Lis

我启动了几个异步进程,如果需要的话,这些进程可以启动更多的进程(想想遍历目录结构之类的东西)。每个进程都会返回一些东西,最后我希望等待所有进程的完成,并安排一个函数来处理结果集合

天真的尝试 我的解决方案尝试使用可变的
ListBuffer
(我一直在向其中添加我生成的未来)和
Future.sequence
来安排一些函数在完成此缓冲区中列出的所有这些未来时运行

我准备了一个简单的例子来说明这个问题:

object FuturesTest extends App {
  var queue = ListBuffer[Future[Int]]()

  val f1 = Future {
    Thread.sleep(1000)
    val f3 = Future {
      Thread.sleep(2000)
      Console.println(s"f3: 1+2=3 sec; queue = $queue")
      3
    }
    queue += f3
    Console.println(s"f1: 1 sec; queue = $queue")
    1
  }
  val f2 = Future {
    Thread.sleep(2000)
    Console.println(s"f2: 2 sec; queue = $queue")
    2
  }

  queue += f1
  queue += f2
  Console.println(s"starting; queue = $queue")

  Future.sequence(queue).foreach(
    (all) => Console.println(s"Future.sequence finished with $all")
  )

  Thread.sleep(5000) // simulates app being alive later
}
它首先调度
f1
f2
期货,然后
f3
将在
f1
分辨率1秒后调度<代码>f3本身将在2秒后解析。因此,我期望得到以下结果:

starting; queue = ListBuffer(Future(<not completed>), Future(<not completed>))
f1: 1 sec; queue = ListBuffer(Future(<not completed>), Future(<not completed>), Future(<not completed>))
f2: 2 sec; queue = ListBuffer(Future(Success(1)), Future(<not completed>), Future(<not completed>))
f3: 1+2=3 sec; queue = ListBuffer(Future(Success(1)), Future(Success(2)), Future(<not completed>))
Future.sequence finished with ListBuffer(1, 2, 3)
这是按计划进行的,最终实现了所有3个未来:

starting; queue = ListBuffer(Future(<not completed>), Future(<not completed>))
f1: 1 sec; queue = ListBuffer(Future(<not completed>), Future(<not completed>), Future(<not completed>))
f2: 2 sec; queue = ListBuffer(Future(Success(1)), Future(<not completed>), Future(<not completed>))
... still waiting for tasks
f3: 1+2=3 sec; queue = ListBuffer(Future(Success(1)), Future(Success(2)), Future(<not completed>))
finished with ListBuffer(1, 2, 3)
启动;queue=ListBuffer(Future(),Future())
f1:1秒;queue=ListBuffer(Future(),Future(),Future())
f2:2秒;queue=ListBuffer(Future(成功(1)),Future(),Future())
... 仍在等待任务
f3:1+2=3秒;队列=ListBuffer(未来(成功(1)),未来(成功(2)),未来()
完成ListBuffer(1、2、3)
但它仍然很难看。它只是重新启动
Future.sequence
等待,如果它看到在完成时队列比结果数长,希望下次完成时情况会更好。当然,这是不好的,因为它会耗尽堆栈,如果在创建未来和将其附加到队列之间的一个小窗口中触发此检查,则可能会出错



有没有可能不用Akka重写所有内容,或者使用
wait.result
(这是因为我的代码是为Scala.js编译的)。

正确的方法可能是编写未来。具体地说,f1不应该仅仅启动f3,它可能应该在它上面进行平面映射——也就是说,直到f3解决,f1的未来才会解决

请记住,
Future.sequence
是一种回退选项,仅在期货真正断开时使用。在您所描述的情况下,存在真正的依赖关系,这些依赖关系在您实际返回的未来中表现得最好。在使用Futures时,flatMap是您的朋友,应该是您最先使用的工具之一。(通常但不总是作为理解的


可以肯定地说,如果您想要一个可变的未来队列,代码的结构可能不正确,有更好的方法可以做到这一点。特别是在Scala.js中(这是我大部分代码所在的地方,也是非常重要的未来),我经常使用它来理解这些未来——我认为这是唯一明智的操作方式…

就像Justin提到的那样,您不能丢失对其他期货中衍生的期货的引用,您应该使用map和flatMap来链接它们

val f1 = Future {
  Thread.sleep(1000)
  val f3 = Future {
    Thread.sleep(2000)
    Console.println(s"f3: 1+2=3 sec")
    3
  }
  f3.map{
    r =>
      Console.println(s"f1: 1 sec;")
      Seq(1, r)
  }
}.flatMap(identity)

val f2 = Future {
  Thread.sleep(2000)
  Console.println(s"f2: 2 sec;")
  Seq(2)
}

val futures = Seq(f1, f2)

Future.sequence(futures).foreach(
  (all) => Console.println(s"Future.sequence finished with ${all.flatten}")
)

Thread.sleep(5000) // simulates app being alive later
这只适用于最小的示例,我不确定它是否适用于您的实际用例。结果是:

f2: 2 sec;
f3: 1+2=3 sec
f1: 1 sec;
Future.sequence finished with List(1, 3, 2)

我不会涉及Future.sequence:它将操作并行化,您似乎在寻找顺序异步执行。此外,您可能不需要在定义后立即启动未来。构图应如下所示:

def run[T](queue: List[() => Future[T]]): Future[List[T]] = {
  (Future.successful(List.empty[T]) /: queue)(case (f1, f2) =>
  f1() flatMap (h => )
  )

val t0 = now

def f(n: Int): () => Future[String] = () => {
  println(s"starting $n")
  Future[String] {
    Thread.sleep(100*n)
    s"<<$n/${now - t0}>>"
  }
}

println(Await.result(run(f(7)::f(10)::f(20)::f(3)::Nil), 20 seconds))
def run[T](队列:List[()=>Future[T]]:Future[List[T]={
(Future.successful(List.empty[T])/:queue)(案例(f1,f2)=>
f1()平面映射(h=>)
)
val t0=现在
def(n:Int):()=>Future[String]=()=>{
打印项次(s“起始$n”)
未来[字符串]{
线程睡眠(100*n)
s“
}
}
println(等待结果(运行(f(7)::f(10)::f(20)::f(3)::Nil),20秒))

诀窍是不要过早地推出期货;这就是为什么我们有
f(n)
,直到我们用
()

调用它才会开始的原因。感谢这个工作示例!在现实生活中,事情变得有点复杂,但总体上它确实是这样工作的:→ 将此标记为答案:)
f2: 2 sec;
f3: 1+2=3 sec
f1: 1 sec;
Future.sequence finished with List(1, 3, 2)
def run[T](queue: List[() => Future[T]]): Future[List[T]] = {
  (Future.successful(List.empty[T]) /: queue)(case (f1, f2) =>
  f1() flatMap (h => )
  )

val t0 = now

def f(n: Int): () => Future[String] = () => {
  println(s"starting $n")
  Future[String] {
    Thread.sleep(100*n)
    s"<<$n/${now - t0}>>"
  }
}

println(Await.result(run(f(7)::f(10)::f(20)::f(3)::Nil), 20 seconds))