What would be a good concurrency primitives for accessing of an object that is CPU bound(without IO and Networking)?
For example, there's a FooCounter, which has a methods get(), set() and inc() for var counter: Int, that being shared among thousands and millions of threads.
object FooCounter{
var counter: Int = 0;
def get() = counter
def set(value: Int) = {counter = counter + value}
def inc() = counter + 1
}
I found that most literature on Scala is oriented about Akka. For me it seems that Actor model is not really suitable for this task.
There's also Futures/Promises but they are good for blocking tasks.
In Java there's a good primitive Atomics that uses latches, which is pretty robust and descent for this task.
Update: I can use Java primitives for this simple task. However, my objective to use and learn Scala Concurrency model on this simple example.
If you're not willing to use java.util.concurrent
directly, then your most elegant solution may be using Akka Agents.
import scala.concurrent.ExecutionContext.Implicits.global
import akka.agent.Agent
class FooCounter {
val counter = Agent(0)
def get() = counter()
def set(v: Int) = counter.send(v)
def inc() = counter.send(_ + 1)
def modify(f: Int => Int) = counter.send(f(_))
}
This is asynchronous, and guarantees sequentiality of operations. The semantics are nice, and if you need more performance, you can always change it to the good old java.util.concurrent
analogue:
import java.util.concurrent.atomic.AtomicInteger
class FooCounter {
val counter = new AtomicInteger(0)
def get() = counter.get()
def set(v: Int) = counter.set(v)
def inc() = counter.incrementAndGet()
def modify(f: Int => Int) = {
var done = false
var oldVal: Int = 0
while (!done) {
oldVal = counter.get()
done = counter.compareAndSet(oldVal, f(oldVal))
}
}
}
You should be aware that your implementation of FooCounter
is not thread-safe. If multiple threads simultaneously invoke the get
, set
and inc
methods, there is no guarantee that the count will be accurate. You should use one of the following utilities to implement a correct concurrent counter:
synchronized
statement (Scala synchronized
statement is similar to the one in Java)Other Scala concurrency utilities, such as futures and promises, parallel collections or reactive extensions, are not the best fit for implementing a concurrent counter, and have different usages.
You should know that Scala in some cases reuses the Java concurrency infrastructure. For example - Scala does not provide atomic variables of its own, since Java atomic classes already do the job. Instead, Scala aims to provide higher-level concurrency abstractions, such as actors, STMs and asynchronous event streams.
To assess the running time and efficiency of your implementation, a good choice is ScalaMeter. The online documentation at the website contains detailed examples on how to do benchmarking.
While Akka is the most popular and best documented Scala concurrency utility, there are many other high quality implementations, some of which are more appropriate for different tasks:
I think Learning Concurrent Programming in Scala might be a good book for you. It contains detailed documentation of different concurrency styles in Scala, as well as directions on when to use which. Chapters 5 and 9 also deal with benchmarking. Specifically, a concurrent counter utility is described, optimized, and made scalable in the Chapter 9 of the book.
Disclaimer: I'm the author.
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