【问题标题】:Executing a simple task on another thread in scala在scala中的另一个线程上执行一个简单的任务
【发布时间】:2010-11-25 02:33:12
【问题描述】:

我想知道是否有一种方法可以在没有太多开销的 scala 中的另一个线程上执行非常简单的任务?

基本上,我想创建一个可以处理执行任意数量任务的全局“执行器”。然后我可以使用执行器来构建额外的构造。

此外,如果客户端不必考虑阻塞或非阻塞因素,那就太好了。

我知道 scala 演员库是建立在 Doug Lea FJ 的基础之上的,而且它们在一定程度上支持我想要完成的工作。但是据我了解,我必须预先分配一个“演员池”才能完成。

我想避免为此创建一个全局线程池,因为据我了解,它并不擅长细粒度并行。

这是一个简单的例子:

import concurrent.SyncVar
object SimpleExecutor {
  import actors.Actor._
  def exec[A](task:  => A) : SyncVar[A] = {
    //what goes here?
    //This is what I currently have
    val x = new concurrent.SyncVar[A]
    //The overhead of making the actor appears to be a killer
    actor {
      x.set(task)
    }
    x
  }
  //Not really sure what to stick here
  def execBlocker[A](task: => A) : SyncVar[A] = exec(task)

}

现在是使用 exec 的示例:

object Examples {
  //Benchmarks a task
  def benchmark(blk : => Unit) = {
    val start = System.nanoTime
    blk
    System.nanoTime - start
  }

  //Benchmarks and compares 2 tasks
  def cmp(a: => Any, b: => Any) = {
    val at = benchmark(a)
    val bt = benchmark(b)
    println(at + " " + bt + " " +at.toDouble / bt)
  }

  //Simple example for simple non blocking comparison
  import SimpleExecutor._
  def paraAdd(hi: Int) = (0 until hi) map (i=>exec(i+5)) foreach (_.get)
  def singAdd(hi: Int) = (0 until hi) foreach (i=>i+5)

  //Simple example for the blocking performance
  import Thread.sleep
  def paraSle(hi : Int) = (0 until hi) map (i=>exec(sleep(i))) foreach (_.get)
  def singSle(hi : Int) = (0 until hi) foreach (i=>sleep(i))
}

最后运行示例(可能要运行几次,以便 HotSpot 可以预热):

import Examples._
cmp(paraAdd(10000), singAdd(10000))
cmp(paraSle(100), singSle(100))

【问题讨论】:

    标签: scala concurrency parallel-processing actor fork-join


    【解决方案1】:

    这就是 Futures 的用途。只需import scala.actors.Futures._,使用future 创建新的future,使用awaitAll 等方法等待结果一段时间,apply 或respond 阻塞直到收到结果,isSet 看看是否准备好与否,等等。

    您也不需要创建线程池。或者,至少,通常情况下你不会。你为什么认为你会这样做?

    编辑

    您无法并行处理像整数加法这样简单的事情,因为这比函数调用还要快。并发只会通过避免因阻塞 i/o 而损失的时间以及使用多个 CPU 内核并行执行任务来带来性能。在后一种情况下,任务的计算成本必须足够高,以抵消划分工作负载和合并结果的成本。

    使用并发的另一个原因是提高应用程序的响应能力。这不是让它更快,而是让它更快地响应用户,而这样做的一种方法是将相对快速的操作卸载到另一个线程,以便处理用户看到或做的事情的线程可以更快。但我离题了。

    您的代码存在严重问题:

      def paraAdd(hi: Int) = (0 until hi) map (i=>exec(i+5)) foreach (_.get)
      def singAdd(hi: Int) = (0 until hi) foreach (i=>i+5)
    

    或者,翻译成期货,

      def paraAdd(hi: Int) = (0 until hi) map (i=>future(i+5)) foreach (_.apply)
      def singAdd(hi: Int) = (0 until hi) foreach (i=>i+5)
    

    你可能认为paraAdd 正在并行执行任务,但事实并非如此,因为Range 具有map 的非严格实现(这取决于Scala 2.7;从Scala 2.8.0 开始, Range 是严格的)。您可以在其他 Scala 问题上查找它。会发生什么:

    1. 从0 到hi 创建了一个范围
    2. 从范围的每个元素 i 创建一个范围投影到一个函数中,该函数在调用时返回 future(i+5)。
    3. 对于范围投影的每个元素(i => future(i+5)),对元素求值(foreach 是严格的),然后在其上调用函数 apply。

    所以,因为future没有在第 2 步中调用,但仅在第 3 步中调用,您将等待每个 future 完成,然后再执行下一个。您可以使用以下方法修复它:

      def paraAdd(hi: Int) = (0 until hi).force map (i=>future(i+5)) foreach (_.apply)
    

    这会给您带来更好的性能,但永远不如简单的立即添加。另一方面,假设您这样做:

    def repeat(n: Int, f: => Any) = (0 until n) foreach (_ => f)
    def paraRepeat(n: Int, f: => Any) = 
      (0 until n).force map (_ => future(f)) foreach (_.apply)
    

    然后比较:

    cmp(repeat(100, singAdd(100000)), paraRepeat(100, singAdd(100000)))
    

    您可能会开始看到收益(这取决于内核数量和处理器速度)。

    【讨论】:

    • 很酷,我会在有机会时进行研究,快速这里是您正在谈论的课程的 scaladoc 的链接:scala-lang.org/docu/files/api/scala/actors/Futures$object.html 我还发现转到链接的源代码很有帮助该页面通常 scaladocs 有点模棱两可。
    • 好吧,我不知道如何让堆栈溢出 cmets 不吐在带有 $s 的 url 上,通过 $object 到该 url 或从此类中选择视图源'Scaladoc @987654322 @
    • 好吧,我看过那景象,我不只是看到它。你能给我代码放入执行体吗?似乎期货没有给我我想要的东西。我会考虑使用线程池,我想我会这样做,因为创建演员的开销会淹没执行操作。当我用这个更改 paraAdd 和 paraSle 函数时: def paraAdd(hi: Int) = (0 until hi) map (i=>future(i+5)) foreach (_()) def paraSle(hi : Int) = (0 到 hi)map (i=>future(sleep(i))) foreach (_()) 我的性能比 paraAdd 的 singAdd 差 100 倍或更多,而 Sle 的性能完全相同
    • 这是并发和线程的内在特性。添加两个值甚至比函数调用更便宜,所以你不可能在这么小的范围内节省时间。只有两种方法可以提高性能:避免阻塞调用(基本上是 i/o),以及使用多个 CPU 内核来并行化任务。我将在编辑我的答案时讨论另一个问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-15
    • 2014-04-03
    • 1970-01-01
    • 1970-01-01
    • 2022-12-31
    • 2017-11-16
    相关资源
    最近更新 更多