【问题标题】:Proper way to access shared resource in Scala actors在 Scala 演员中访问共享资源的正确方法
【发布时间】:2009-10-29 17:31:18
【问题描述】:

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

我想知道“Scala Actors”的执行方式是如何工作的。

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

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

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

代码:

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)
  }
}

Future 版本似乎最干净,除了我可以永远阻塞。

有什么想法,或者我对此的整个概念是错误的吗?

【问题讨论】:

    标签: scala concurrency actor


    【解决方案1】:

    这可能是一种糟糕的风格,但一种方法是混合命令式和函数式风格,让您的演员(需要连接)直接插入连接池并使用同步来获得Connection。老实说,我真的不明白这种方法有什么问题。我更喜欢它而不是 !!!? 一个,它只是尖叫 deadlock(甚至 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)
    

    【讨论】:

    • 我实际上使用连接池作为询问参与者和访问共享资源的方法的一种方式。您是说不应该使用演员来访问共享资源吗?
    • 不——我不是这么说的。但是,可以向多个参与者注入同一个连接池,并在适当的地方使用synchronization 来获取连接。您描述的任何其他方法也是有效的,但我肯定会避免对演员的任何阻塞调用。
    • 我想我的问题来自“线程和同步很难/困难/不是很好,这就是 Scala 实现 Actors 的原因”学校。也就是说,有人告诉我 Scala 使用 Actors 解决并发问题。那么,在这个非常简单的情况下,模式是什么?第三个版本是唯一避免阻塞的版本,但对我来说似乎很笨拙
    • @davetron5000 - 我完全同意。请注意,actor 不需要同步 - 资源的同步(如邮箱和actor的内部状态)在sendreceive/@ 中为您完成987654330@ 方法。
    • “绕过同步的需要”我的意思是“绕过我在我的代码中放入 synchronize 关键字的需要”
    【解决方案2】:

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

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

    【讨论】:

    • 这样做的问题是,如果你有一个Persistence 演员,你就会引入一个瓶颈。但是如果你想要一个ConnectionProvider 演员并希望避免阻塞呼叫,没有不错的解决方案
    • 对,连接本身不是共享资源;连接池是;这就是我使用 Actor 来拥有资源池的原因。我想知道 Actor 模型如何使用 WRT 从池中访问资源。
    【解决方案3】:

    Scala actor to non-actor interaction (or synchronizing messages from an actor to a servlet) 的答案所示,您可以使用 !?(timeout, message) 接收 Some(answer) 或 None 如果超时。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-10
      • 2015-06-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-05
      相关资源
      最近更新 更多