Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Concurrency Primitives in Scala

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.

like image 293
Sayat Satybald Avatar asked Nov 13 '14 06:11

Sayat Satybald


Video Answer


2 Answers

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))
    }
  }
}
like image 108
ponythewhite Avatar answered Oct 16 '22 06:10

ponythewhite


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)
  • atomic variables
  • STMs (for example, ScalaSTM)
  • potentially, you could use an Akka actor that is a counter, but note that this is not the most straightforward application of actors

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.

Benchmarking

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.

Documentation and concurrency libraries

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:

  • futures and promises
  • parallel collections and ScalaBlitz
  • software transactional memory
  • reactive extensions

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.

like image 24
axel22 Avatar answered Oct 16 '22 04:10

axel22