开发者

The strange case of multiple Futures in Scala

开发者 https://www.devze.com 2023-03-11 09:27 出处:网络
What\'s the the connection between开发者_JS百科 those Future-related class and traits in Scala, and why are they sprinkled over different packages?

What's the the connection between开发者_JS百科 those Future-related class and traits in Scala, and why are they sprinkled over different packages?

I have found those:

abstract class scala.actors.Future
object         scala.actors.Futures
trait/object   scala.collection.parallel.FutureThreadPoolTasks
trait          scala.concurrent.FutureTaskRunner
trait          scala.parallel.Future    (the package consists of only this file...)

Do they significantly different things or is there another reason why they can't be consolidated?

Is there a good example showing when one would use the one thing or the other?

Edit: Bounty for explaining what each of the classes/traits/objects does and how they justify their existance/how they are useful.


scala.actors._

abstract class Future

First of all, lets see what the documentation says:

A function of arity 0, returing a value of type T that, when applied, blocks the current actor (Actor.self) until the future's value is available.

And that is basically all there is. If you are communicating with an Actor from anywhere outside of another actor (which can receive asynchronous replies to messages simply with another message, using the sender reference) and you need the reply to a sent message, you have two choices:

  • Send a blocking message which waits until the Actor is done computing your result
  • Send a message using a method which returns a Future, and block on that Future only if you really need the value in question (which by then may have alreay been computed)

So a Future is a placeholer for a value which does not yet exist, but probably will in the near future. The following is also interesting:

A future can be queried to find out whether its value is already available without blocking [using "isSet"].

This allows you to do whatever you want until the value you need has been computed/fetched, and you can periodically check if the value has become available.

When digging a bit into the Scala library source code, I found out that Futures are actually just actors. Future itself is a an abstract class, which is extended by the private class FutureActor. This last class is the one that actually implements the Future-functionality.

object Futures

The object Futures is by far not as interesting, as it is merely a container for the "Methods that operate on futures", the handy factory method future which asynchronously evaluates the passed block, returning a future representing the result. A small example would be this:

import scala.actors.Futures
val f = Futures.future {
    println("Start: inside block")
    val s = System.currentTimeMillis
    while(System.currentTimeMillis < (s + 1000)) {
        // Simulate computation
    }
    println("Start: end of block")
    42
}
println("After future")
println(f())
println("The end")

Which should result in something like

Start: inside block
After future
Start: end of block
42
The end

This demonstrates that the future-call does not block the following code until you actually try to retrieve the value of the future (note that the output is non-deterministic. After future could also appear at the beginning of the output).

scala.collection.parallel

This packages is new to Scala 2.9.x and implements parallel counterparts for some of our favorite collections. These all start with Par:

  • ParIterable
  • ParSeq
  • ParSet
  • ParMap

As you may have known or guessed already, these collections implement all possible operations in a parallel manner without you having to worry about it. A small demonstration:

(1 to 10).par.map { b => print(b + " "); b * b }
3 1 6 2 7 4 5 9 10 8
    # => (1, 4, 9, 16, 25, 36, 49, 64, 81, 100)

The result will always be the same, but the order in which the elements are processed is again non-deterministic. Also, if you are on a multicore system, you will probably experience a nice performance boost for larger collections.

trait FutureThreadPoolTasks

The FutureThreadPoolTasks trait extends the Tasks trait, so lets take a look at that one first. The comment above the trait says:

A trait that declares task execution capabilities used by parallel collections.

Judging from the other source comments and the methods found in the Tasks trait, a Task represents a unit of work which needs to be computed. Depending on wether or not a problem is divisible further and if there are more resources available, a Task can split up a Task further by creating more tasks.

Now the FutureThreadPoolTasks trait itself is just a way to compute tasks, which uses the java.util.concurrent.Future class for its synchronization, that is, it does not use scala.actors.Future! From the source:

An implementation of tasks objects based on the Java thread pooling API and synchronization using futures.

object FutureThreadPoolTasks

Once again not very spectacular, just a companion object containing a few (actually only three) utility methods which the FutureThreadPoolTasks trait uses.

scala.concurrent

The documentation on these classes is really bad and apparently there are very few if any (I didn't find a single one) examples which demonstrate the usage of these classes. I will definitely try to gather more information on these and expand on this section as soon as I can!

scala.parallel

trait Future

This seems to be a "Work in progess", as the scala.parallel package only contains this trait. From what I can tell, this is going to be related to a Future implementation which does not use Actors, but that is just a guess. The signature of the trait is the following

trait Future[@specialized +R] extends (() => R)

I am not even going to try to explain the @specialized annotation or variances (the + before the generic R type), but the basic idea in this trait is that a Future is a function which, when executed, returns the value (and must therefor block if it has not been computed yet).

Also, there are only two methods inside the trait itself, apply and isDone. My guess is that isDone, just like the scala.actors.Future.isSet, is supposed to be a non-blocking call to see if the value has been computed, and the apply method should be used to actually retrieve the value.


Below is a brief explanation. (Parts are copied from the scala doc). Let me know if there is something that you dont understand and I will try to be more specific and give you a concrete example.

abstract class scala.actors.Future -- Are you familiar with java.util.concorrent.Future? scala.actors.Future basically represents the result of an asynchronous computation but for actors.

scala.actors.Futures -- An object (~singleton) that contains four utility methods for handling scala.actors.Future.

scala.parallel.Future -- (This one was new to me, but it contains very basic operations (apply and isDone)) its a function without parameters that will block the caller if the parallel computation associated with the function is not completed. (function? hint: extends () )

scala.collection.parallel.FutureThreadPoolTasks -- From the scala doc: "An implementation of tasks objects based on the Java thread pooling API and synchronization using futures." Is that enough? :)

scala.concurrent.FutureTaskRunner -- Are you familiar with Executor? ExecutorScheduler is one (out of three) concrete implementations in the scala standard library. executeFromActor is one of the more interesting methods and should give you a hint about when you need to use this one

0

精彩评论

暂无评论...
验证码 换一张
取 消