Правильный способ доступа к общему ресурсу в Scala участниках
В Java можно синхронизировать методы или блоки, которые обращаются к общему ресурсу, который необходим в многопоточной среде.
Мне интересно, как будет работать способ Scala Actors ".
Предположим, что у меня есть пул соединений объектов java.sql.Connection
, к которому я хочу предоставить потокобезопасный доступ. Я реализую его как актера, который получает сообщения и отправляет отправителю соединение.
Кажется, есть три способа сделать это:
- Использовать будущее
- Используйте
!?
- Пусть класс, требующий
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)
}
}
Будущая версия кажется чистейшей, за исключением того, что я могу заблокировать навсегда.
Любые мысли, или моя вся концепция этого неправильного?
Ответы
Ответ 1
Это может быть плохой стиль, но одним из способов является сочетание императивных и функциональных стилей, если ваш актер (который требует подключения) должен подключить пул соединений напрямую и использовать синхронизацию, чтобы получить 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)
Ответ 2
Актеры делают это не совместное использование ресурсов. Получите доступ к одному персонажу, чья работа связана с доступом к общему ресурсу.
Таким образом, сам ресурс не разделяется между потоками. Актер.
Ответ 3
Как видно из ответа Scala актера на неактивное взаимодействие (или синхронизация сообщений от актера до сервлета), вы можете использовать!? ( тайм-аут, сообщение), чтобы получить Some (ответ) или None, если время ожидания.