在Scala actor中访问共享资源的正确方法

时间:2009-10-29 17:31:18

标签: scala concurrency actor

在Java中,可以同步访问多线程环境中所需的共享资源的方法或块。

我想知道“Scala Actors”这样做的方式是如何运作的。

假设我有一个java.sql.Connection个对象的连接池,我希望提供对它的线程安全访问。我将它实现为接收消息并向发送者发回连接的actor。

似乎有三种方法可以做到这一点:

  1. 使用未来
  2. 使用!?
  3. 让需要Connection的班级成为演员
  4. 代码:

    sealed abstract class ConnectionPoolMessage
    case class NewConnection extends ConnectionPoolMessage
    case class CloseConnection(c:Connection) extends ConnectionPoolMessage
    
    class ConnectionPool extends Actor {
      def act() {
        while (true) {
          receive() {
            case NewConnection => sender ! getConnectionFromPool
            case CloseConnection(conn) => returnConnection(conn)
          }
        }
      }
    }
    
    // Here, my "do stuff" method is all in one place, and I block waiting
    // on the Future from the pool; however this could take forever and cause trouble
    class UsingFuture {
      val pool = new ConnectionPool
      def doSomething() {
        val connectionFuture = pool !! NewConnection
        val connection = connectionFuture() // concerned that we can't timeout here
        // do stuff with my Connection instance
        pool ! CloseConnection(connection)  
      }
    }
    
    
    // here, I wait and just use a timeout
    // Seems clean to me, I guess.
    class UsingBangQuestion {
      val pool = new ConnectionPool
      def doSomething() {
        pool !?(TIMEOUT,NewConnection) match {
          case Some(conn) => {
            // do something with connection
            pool ! CloseConnection(conn)
          }
          case None => throw new RuntimeException("timed out")
        }
      }
    }
    
    // here, I don't worry about timeouts, cause I only use the
    // the connection when I receive a message back with it.  
    // The problem is that I now have to split my logic up
    // with two methods
    class AsAnActor extends Actor {
      val pool = new ConnectionPool
      def startSomething() {
        start
        pool ! NewConnection
      }
      def act() {
        receive() {
          case conn:Connection => finishSomething(conn)
        }
      }
      def finishSomething(conn:Connection) {
        // do stuff with my Connection
        pool ! CloseConnection(conn)
      }
    }
    

    未来版本似乎最干净,除了我可以永远阻止的事实。

    任何想法,还是我对这个错误的全部概念?

3 个答案:

答案 0 :(得分:2)

这可能是糟糕的风格,但一种方法是通过让您的actor(需要连接)直接插入连接池并使用同步来获取Connection来混合命令式和功能性样式。说实话,我真的不明白这种方法有什么问题;我更喜欢!!!?一个,只是尖叫死锁(甚至 livelock )!

我想另一种方法是向您的池发送一条消息,表示需要完成连接的工作以及结果的可能目标:

class DbWork(f: Connection => Unit)
class DbWorkWithResult[T](f:Connection => T, target: OutputChannel[Any])

然后你可以这样使用它:

pool ! new DbWork( { (conn: Connection) => //do something 
                 })

或者:

pool ! new DbWorkWithResult[Int]( (conn: Connection) => //return int
                 }, self)

答案 1 :(得分:0)

Actors的做法不是共享资源。将所有访问权限提供给单个Actor,其作业是处理对共享资源的访问。

这样,资源本身不会在线程之间共享。演员是。

答案 2 :(得分:0)

Scala actor to non-actor interaction (or synchronizing messages from an actor to a servlet)的答案所示,如果超时,您可以使用!?(超时,消息)接收部分(答案)或无。