In Java, one would synchronize methods or blocks that access a shared resource that is needed in a multi-threaded environment.
I'm wondering how the "Scala Actors" way of doing this would work.
Suppose I have a connection pool of java.sql.Connection
objects that I wish to provide thread-safe access to. I implement it as an actor that receives messages and sends the sender a connection back.
It seems there's three ways to do this:
!?
Connection
also be an actorCode:
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)
}
}
The Future version seems cleanest, except for the fact that I could block forever.
Any thoughts, or is my whole conception of this wrong?
It may be bad style but one way is to mix imperative and functional styles by having your actor (which requires connections) to have the connection pool plugged in directly and use synchronization to get a Connection
. To be honest, I don't really see what is wrong with this approach; I much prefer it to the !!
or !?
one, which just screams deadlock (or even livelock)!
I guess one other way would be to send a message to your pool which expressed the work which needed to be done with the connection and a possible target for the result:
class DbWork(f: Connection => Unit)
class DbWorkWithResult[T](f:Connection => T, target: OutputChannel[Any])
And then you might use it thus:
pool ! new DbWork( { (conn: Connection) => //do something
})
Or:
pool ! new DbWorkWithResult[Int]( (conn: Connection) => //return int
}, self)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With