Mantaining type information on the queue of tasks of a ThreadPool
Mantaining type information on the queue of tasks of a ThreadPool
I am self learning concurrency with the 2 very helpful books:
- Concurrency in the JVM, from Brian Goetz.
- Learning Concurrent Programming in Scala, from Aleksandar Prokopec.
As a test to my knowledge, I am trying to build a bare ThreadPool with a fixed number of threads.
This threads are polling on a queue of tasks.
The ThreadPool provides me a method to insert tasks in a queue, returning a MyFutureTask[T], which I hope is an emulation of the actual FutureTask[T] in java, so that I can retrieve the value later.
MyFutureTask[T]
FutureTask[T]
def addTask[T](theTask: () => T): MyFutureTask[T] = queue.synchronized
val myFutureTask: MyFutureTask[T] = new MyFutureTask[T]
override val task: () => T = theTask
queue.enqueue(myFutureTask)
queue.notify()
myFutureTask
If I do not care about the return value of the tasks I submit (i.e. Runnable) then I can have a proper type for the queue of tasks, namely MyFutureTask[Unit]:
private val queue = mutable.Queue[MyFutureTask[Unit]]()
MyFutureTask[Unit]
private val queue = mutable.Queue[MyFutureTask[Unit]]()
However, when the tasks return a value that I later want to retrieve, this would require the tasks queue not to have a proper type, as I would need to submit to the ThreadPool multiple tasks, each with a different return type ( task1 : () => String, task2: () => Int, task3: () => SomeProperType ... ), which would lead to:
() => String
() => Int
() => SomeProperType
private val tasks = mutable.Queue[MyFutureTask[_]]()
private val tasks = mutable.Queue[MyFutureTask[_]]()
This leaves me uneasy, as in Scala everything that is not typed is frowned upon.
So my questions are:
1 - Have I said anything wrong above? Am I missing some import step? Or is this not the correct approach at all?
2 - Is it unavoidable for the queue of tasks not to have a proper type in the actual ThreadPool implementations?
3 - If it is unavoidable, are there any disadvantages to it? Is that even a concern?
Thanks,
2 Answers
2
This is related to "type erasure" in the JVM that is inherited by all the languages that run on the JVM. In short, generics are checked by the compiler and then they are erased, so if you want a collection of mixed type, the collection's type-parameter must be the super-class of all the possible classes. And yes, when you retrieve the data from the collection you are left with the super class.
I think that Shapeless's HList let you keep multiple types in a list.
Otherwise, you need to cast. If I have to, I use a function similar to this:
def dequeue[T](tasks: mutable.Queue[MyFutureTask[Any]]) = tasks.dequeue().asInstanceOf[MyFutureTask[T]]
I don't see why your queue needs to know about a the return type at all.
Just make it operate on Runnables:
Runnables
def addTask[T](theTask: () => T): Future[T] =
val result = Promise[T]()
val myFutureTask: Runnable = new Runnable
override val run()
result.complete(Try theTask() )
queue.synchronized
queue.enqueue(myFutureTask)
queue.notify()
result.future
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.