Concurrency Primitives in Scala

What good concurrency primitives are for accessing an object that is connected to the CPU (without I / O and network)?

For example, there is a FooCounter that has get (), set (), and inc () methods for the var: Int counter, which is shared between 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 of the Scala literature is focused on Akka. It seems to me that the Actor model is not suitable for this task.

Futures / Promises also exist, but they are good for blocking tasks.

Java has a nice primitive Atomics that uses gate valves, which is pretty stable and suitable for this task.

Update: I can use Java primitives for this simple task. However, my goal is to use and learn the Scala concurrency model with this simple example.

+5
source share
5 answers

You should be aware that your implementation of FooCounter not thread safe. If multiple threads call the get , set and inc methods at the same time, there is no guarantee that the count will be accurate. To implement the correct parallel counter, you must use one of the following utilities:

  • Operator
  • synchronized (Scala synchronized statement is similar to one in Java )
  • atomic variables
  • STM (e.g. ScalaSTM )
  • perhaps you can use the accra Akka, which is the counter, but note that this is not the easiest application of the actors.

Other Scala concurrency utilities, such as futures and promises, parallel collections, or reactive extensions, are not suitable for implementing a parallel counter and have different uses.

You should be aware that Scala reuses the Java concurrency framework in some cases. For example, Scala does not provide its own atomic variables, since Java classes already do the job. Instead, Scala aims to provide higher level abstractions of concurrency, such as subjects, STMs, and asynchronous event streams.

Benchmarking

ScalaMeter is a good choice to evaluate the uptime and effectiveness of your implementation. The online documentation on the website contains detailed examples of how to benchmark.

Documentation and concurrency libraries

While Akka is the most popular and most well-documented Scala concurrency utility, there are many other high-quality implementations, some of which are more suitable for different tasks:

I think Scala Learning Parallel Programming might be a good book for you. It contains detailed documentation on the various concurrency styles in Scala, as well as instructions on when to use them. Chapters 5 and 9 also deal with benchmarking. In particular, the scalable parallel counter utility in chapter 9 of the book is described, optimized, and executed.

Disclaimer: I am the author.

+7
source

You can use all Java syntonization primitives, such as AtomicInteger, for example, for counting.

For more complex tasks, I personally like the scala -stm library: http://nbronson.imtqy.com/scala-stm/

With STM, your example will look like this

 object FooCounter{ private val counter = Ref(0); def get() = atomic { implicit txn => counter() } def set(value: Int) = atomic { implicit txn => counter() = counter() + value } def inc() = atomic { implicit txn => counter() = counter() + 1 } } 

However, for this simple example, I settled on Java primitives.

+3
source

You are right about the "orientation of Akka" in Scala: in general, I think that there are quite a lot of coincidences between the community of developers of the Scala language and Akka. Later versions of Scala rely on Akka actors for concurrency, but frankly, I see nothing wrong with that.

Regarding your question, to bring the book "Akka in action":

Actors are great at handling many messages, capturing state and reacting with different behaviors based on the messages they receive. "

and

"Futures is a tool to use when you prefer to use functions and you don’t really need objects to complete the task."

The future is a placeholder for a value that is not yet available, so even if it is used for non-blocking tasks, it is useful for more than that, and I think that it may be your choice here.

+3
source

One possible solution to your problem is actors. This is not a quick fix, but safe and simple:

 sealed trait Command case object Get extends Command case class Inc(value: Int) extends Command class Counter extends Actor { var counter: Int = 0 def receive = { case Get => sender ! counter case Inc(value: Int) => counter += value sender ! counter } } 
+2
source

If you do not want to use java.util.concurrent directly, then the most elegant solution might be 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(_)) } 

It is asynchronous and guarantees a sequence of operations. The semantics are good, and if you need more performance, you can always change it to the good old analogue of java.util.concurrent :

 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)) } } } 
+2
source

Source: https://habr.com/ru/post/1206777/


All Articles