如何与ZIO并行优雅地执行多种效果

时间:2019-07-13 07:38:34

标签: scala scalaz scalaz-zio

我知道我可以使用

import zio.Task

def zip3Par[A, B, C](a: Task[A], b: Task[B], c: Task[C]): Task[(A, B, C)] =
  a.zipPar(b).zipWithPar(c) { case ((a, b), c) => (a, b, c) }

def zip4Par[A, B, C, D](a: Task[A], b: Task[B], c: Task[C], d: Task[D]): Task[(A, B, C, D)] =
  zip3Par(a, b, c).zipWithPar(d) { case ((a, b, c), d) => (a, b, c, d) }

要并行执行3或4个任务,但是如果有一个更优雅的解决方案,我会受伤吗?

3 个答案:

答案 0 :(得分:3)

您可以仅将ZIO.collectAllPar与任务列表一起使用:

def collectTasks(tasks: Task[Int]*):Task[List[Int]] = ZIO.collectAllPar(tasks)

然后您可以像使用它:

val t1 = Task.effect{
  Thread.sleep(100)
  println("t1 started")
  Thread.sleep(1000)
  1
}

val t2 = Task.effect{
  println("t2 started")
  Thread.sleep(1000)
  2
}


val t3 = Task.effect{
  println("t3 started")
  Thread.sleep(1000)
  3
}

(new DefaultRuntime() {}).unsafeRun(collectTasks(t1,t2,t3))

,它将同时运行您的所有任务。

使用元组而不是列表的通用解决方案如果没有定形,将很难在 Scala 2 中实现。它会在 Scala 3 中更改,因为这样它们就可以作为异构列表来处理。

答案 1 :(得分:3)

除了KrzysztofAtłasik的答案外,还有 collectAllParN ,其功能类似于collectAllPAr,但您可以指定要使用的最大光纤数:

 val a = Task {
      println("t1 started")
      Thread.sleep(2000)
      println("t1 finished")
      1
    }
    val b = Task {
      println("t2 started")
      Thread.sleep(1000)
      println("t2 finished")
      2
    }
    val c = Task {
      println("t3 started")
      Thread.sleep(3000)
      println("t3 finished")
      3
    }
    val d = Task {
      println("t4 started")
      Thread.sleep(1000)
      println("t4 finished")
      4
    }

您可以这样运行:

 Task.collectAllParN(4)(List(a, b, c, d))

如果您有许多(数百或数千)并行任务,这特别有用,这样可以避免溢出和内存错误。继续,将要使用的光纤数量更改为2或3,然后亲自查看执行如何变化。

并行执行的另一种选择是,一旦您的消费者收到任务,就将任务放在 ZQueue fork 上。

答案 2 :(得分:3)

还要注意,有class App extends React.Component { constructor() { super(); this.state = { value: true, countOfClicks: 0 }; this.pickRandom = this.pickRandom.bind(this); } pickRandom() { this.setState({ value: Math.random() > 0.5, // randomly picks true or false countOfClicks: this.state.countOfClicks + 1 }); } // comment out the below to re-render on every click shouldComponentUpdate(nextProps, nextState) { return this.state.value != nextState.value; } render() { return ( <div> shouldComponentUpdate demo <p><b>{this.state.value.toString()}</b></p> <p>Count of clicks: <b>{this.state.countOfClicks}</b></p> <button onClick={this.pickRandom}> Click to randomly select: true or false </button> </div> ); } } ReactDOM.render( <App />, document.getElementById('app') ); 组合器。这是<&>的别名。这将生成一个元组,如果您用于理解,我建议您看看zipPar,它可以解决元组问题的理解

下面是将better-monadic-for组合器与map结合使用的示例:

<&>

(t1 <&> t2 <&> t3 <&> t4) map { case i1 <*> i2 <*> i3 <*> i4 => s"$i1, $i2, $i3, $i4" }ZIO.collectAllPar仅在所有ZIO.collectAllParN具有相同的返回类型时才起作用。那不是问题。