Управление типом информации о очереди задач ThreadPool - PullRequest
0 голосов
/ 25 августа 2018

Я изучаю параллелизм с двумя очень полезными книгами:
- Параллелизм в JVM , от Брайана Гетца.
- Обучение параллельному программированию в Scala , от Александар Прокопец.

В качестве проверки, насколько мне известно, я пытаюсь создать чистый ThreadPool с фиксированным числом потоков.
Эти потоки опрашивают очередь задач.
ThreadPool предоставляет мне метод для вставки задач в очередь, возвращая MyFutureTask[T], который, я надеюсь, является эмуляцией фактического FutureTask[T] в Java, чтобы я мог получить значение позже.

 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
 }

Если меня не волнует возвращаемое значение заданий, которые я отправляю (т.е. Runnable), тогда у меня может быть правильный тип для очереди задач, а именно MyFutureTask[Unit]:
private val queue = mutable.Queue[MyFutureTask[Unit]]()

Однако, когда задачи возвращают значение, которое я позже хочу получить, для этого потребуется, чтобы очередь задач не имела правильного типа, так как мне нужно было бы передать в ThreadPool несколько задач, каждая с различным типом возврата ( задача 1: () => String, задача 2: () => Int, задача 3: () => SomeProperType ...), что приведет к:

private val tasks = mutable.Queue[MyFutureTask[_]]()

Это заставляет меня чувствовать себя неловко, так как в Scala все, что не напечатано, осуждается.

Итак, мои вопросы:
1 - Я сказал что-то не так выше? Я пропустил какой-то шаг импорта? Или это совсем не правильный подход?
2 - Неизбежно ли, что очередь задач не имеет правильного типа в реальных реализациях ThreadPool?
3 - Если это неизбежно, есть ли у него недостатки? Это даже беспокойство?

Спасибо

Ответы [ 2 ]

0 голосов
/ 26 августа 2018

Я не понимаю, почему вашей очереди вообще нужно знать тип возвращаемого значения. Просто заставьте его работать на 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
 }
0 голосов
/ 25 августа 2018

Это связано с «стиранием типов» в JVM, которое наследуется всеми языками, которые работают на JVM.Короче говоря, дженерики проверяются компилятором, а затем стираются, поэтому, если вы хотите коллекцию смешанного типа, параметр типа коллекции должен быть суперклассом всех возможных классов.И да, когда вы извлекаете данные из коллекции, у вас остается суперкласс.

Я думаю, что HList Shapeless позволяет вам хранить несколько типов в списке.

В противном случае вам нужнобросать.Если мне нужно, я использую функцию, подобную этой:

def dequeue[T](tasks: mutable.Queue[MyFutureTask[Any]]) = tasks.dequeue().asInstanceOf[MyFutureTask[T]]
...