【问题标题】:Sleeping actors?睡觉的演员?
【发布时间】:2009-08-03 20:05:35
【问题描述】:

让演员睡觉的最佳方式是什么?我将参与者设置为想要维护数据库的不同部分(包括从外部来源获取数据)的代理。出于多种原因(包括不使数据库或通信超载和一般负载问题),我希望参与者在每个操作之间休眠。我正在查看大约 10 个演员对象。

actor 将无限运行,因为总会有新数据进入,或者坐在表中等待传播到数据库的其他部分等。我们的想法是让数据库尽可能完整在任何时间点。

我可以使用无限循环来执行此操作,并在每个循环结束时休眠,但根据http://www.scala-lang.org/node/242,演员使用线程池,只要所有线程都被阻塞,就会扩展该线程池。因此,我认为每个参与者中的 Thread.sleep 都是一个坏主意,因为这会不必要地浪费线程。

也许我可以有一个有自己的循环的中心参与者,它在时钟上向订阅者发送消息(如异步事件时钟观察者)?

有没有人做过类似的事情或有什么建议?很抱歉提供额外(可能是多余的)信息。

干杯

【问题讨论】:

    标签: scala actor


    【解决方案1】:

    第一个答案中提到了 Erlang 的一个好点,但它似乎消失了。你可以很容易地用 Scala 演员做同样的类似 Erlang 的把戏。例如。让我们创建一个不使用线程的调度程序:

    import actors.{Actor,TIMEOUT}
    
    def scheduler(time: Long)(f: => Unit) = {
      def fixedRateLoop {
        Actor.reactWithin(time) {
          case TIMEOUT => f; fixedRateLoop
          case 'stop => 
        }
      }
      Actor.actor(fixedRateLoop)
    }
    

    让我们使用一个测试客户端actor来测试它(我在 Scala REPL 中做到了):

    case class Ping(t: Long)
    
    import Actor._
    val test = actor { loop {
      receiveWithin(3000) {
        case Ping(t) => println(t/1000)
        case TIMEOUT => println("TIMEOUT")
        case 'stop => exit
      }
    } }
    

    运行调度程序:

    import compat.Platform.currentTime
    val sched = scheduler(2000) { test ! Ping(currentTime) }
    

    你会看到这样的东西

    scala> 1249383399
    1249383401
    1249383403
    1249383405
    1249383407
    

    这意味着我们的调度程序按预期每 2 秒发送一条消息。让我们停止调度程序:

    sched ! 'stop
    

    测试客户端将开始报告超时:

    scala> TIMEOUT
    TIMEOUT
    TIMEOUT
    

    也停止它:

    test ! 'stop
    

    【讨论】:

      【解决方案2】:

      没有必要显式地让actor休眠:对每个actor使用loopreact意味着底层线程池将有等待线程,而没有消息供actor处理。

      如果您想安排事件让演员处理,这很容易使用来自java.util.concurrent 实用程序的单线程调度程序:

      object Scheduler {
        import java.util.concurrent.Executors
        import scala.compat.Platform
        import java.util.concurrent.TimeUnit
        private lazy val sched = Executors.newSingleThreadScheduledExecutor();
        def schedule(f: => Unit, time: Long) {
          sched.schedule(new Runnable {
            def run = f
          }, time , TimeUnit.MILLISECONDS);
        }
      }
      

      您可以扩展它以执行周期性任务,并且可以这样使用它:

      val execTime = //...  
      Scheduler.schedule( { Actor.actor { target ! message }; () }, execTime)
      

      然后,您的目标参与者将只需要实现一个适当的react 循环来处理给定的消息。没有必要让任何演员睡觉。

      【讨论】:

        【解决方案3】:

        来自 lift-util 的 ActorPing(Apache 许可证)有 schedule 和 scheduleAtFixedRate 来源: ActorPing.scala

        来自 scaladoc:

        ActorPing 对象安排一个演员在特定的时间间隔用给定的消息 ping。 schedule 方法返回一个 ScheduledFuture 对象,如果需要,可以取消该对象

        【讨论】:

          【解决方案4】:

          很遗憾,oxbow_lakes 的回答有两个错误。

          一个是简单的声明错误(long time vs time: Long),但第二个更微妙。

          oxbow_lakes 声明运行为

          def run = actors.Scheduler.execute(f) 
          

          然而,这会导致消息不时消失。那就是:他们被安排但永远不会发送。声明运行为

          def run = f
          

          为我修好了。在 lift-util 的 ActorPing 中是完全一样的。

          整个调度器代码变成:

          object Scheduler {
              private lazy val sched = Executors.newSingleThreadedScheduledExecutor();
              def schedule(f: => Unit, time: Long) {
                  sched.schedule(new Runnable {
                    def run = f
                  }, time - Platform.currentTime, TimeUnit.MILLISECONDS);
              }
          }
          

          我尝试编辑 oxbow_lakes 帖子,但无法保存(损坏?),我还没有评论的权利。因此,一个新的职位。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-10-19
            • 1970-01-01
            • 1970-01-01
            • 2021-10-11
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多