Правильный способ доступа к общему ресурсу в актерах Scala - PullRequest
10 голосов
/ 29 октября 2009

В 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 кажется самой чистой, за исключением того факта, что я могу заблокировать навсегда.

Есть какие-нибудь мысли или все мое представление об этом неверно?

Ответы [ 3 ]

2 голосов
/ 29 октября 2009

Это может быть плохой стиль, но один из способов состоит в том, чтобы смешать императивный и функциональный стили, если ваш актер (для которого требуются соединения) подключит пул соединений напрямую и использует синхронизацию для получения 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)
0 голосов
/ 11 января 2011

Как видно из ответа на Scala-взаимодействие между актером и не-актером (или синхронизацией сообщений от актера к сервлету) , вы можете использовать!? (Время ожидания, сообщение), чтобы получить некоторые (ответ) или Нет, если истекло время ожидания.

0 голосов
/ 30 октября 2009

Актерский способ сделать это не делиться ресурсами. Получить доступ ко всем субъектам, чья работа заключается в том, чтобы иметь доступ к общему ресурсу.

Таким образом, сам ресурс не распределяется между потоками. Актер.

...