简体   繁体   中英

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.

 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 (ie Runnable) then I can have a proper type for the queue of tasks, namely 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:

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,

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 :

 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
 }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM