Как этот тупик случается в Scala Future? - PullRequest
0 голосов
/ 12 мая 2018

Этот фрагмент взят из документа Monix .Это пример того, как ввести тупик в Scala.

import java.util.concurrent.Executors
import scala.concurrent._
implicit val ec = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(1))

def addOne(x: Int) = Future(x + 1)

def multiply(x: Int, y: Int) = Future {
  val a = addOne(x)
  val b = addOne(y)
  val result = for (r1 <- a; r2 <- b) yield r1 * r2

  // This can dead-lock due to the limited size of our thread-pool!
  Await.result(result, Duration.Inf)
}

Я понимаю, что делает код, но не о том, как он выполняется.

Почему это строка Await.result(result, Duration.Inf), вызывающаятупик?(Да, я проверял это)

Разве это не то, что самые внешние Future в функции умножения занимают весь пул потоков (единственный) и, следовательно, тупик (потому что будущее addOne навсегда блокируется при ожидании потока)?

Ответы [ 3 ]

0 голосов
/ 12 мая 2018

Прежде всего, я бы сказал этот код может имитировать тупик, не гарантируется, что он всегда будет в тупике. Что происходит в приведенном выше коде. У нас есть только один поток в пуле потоков. И как только мы вызываем множественную функцию, так как это будущее, поэтому она должна запускаться в отдельном потоке, скажем, мы назначаем для этой функции единственный поток, который у нас есть в пуле потоков. Теперь функция addOne также является будущим, поэтому она снова начнет работать в том же потоке, но не будет ждать завершения a = addOne и перехода к следующей строке b = addOne, следовательно, тот же поток, который выполнял a = addOne теперь выполнение b = addOne и значение all никогда не будет вычислено, и это будущее не будет полным и никогда не будет завершено, так как у нас есть только один поток, тот же случай со строкой b = addOne, которую он контролирует, не будет ждать завершения этого Будущее и переход к циклу for также является асинхронным в Scala, поэтому он снова не будет оцениваться и будет ожидать последней строки и будет ждать бесконечное количество времени для завершения предыдущего фьючерса.

Необходимое и достаточное условие, чтобы попасть в мертвый замок.

  1. Условие взаимного исключения
  2. Состояние удержания и ожидания
  3. Не выгрузка
  4. Круговое ожидание

Здесь мы видим, что у нас есть только один поток, поэтому процессы, которые будут выполняться, не являются взаимоисключающими.

как только поток выполняет определенный блок и, следовательно, это будущее, и он не ожидает его завершения, он продолжает и выполняет следующий блок, следовательно, он достигает оператора await, и поток удерживается там, пока все другие будущие не завершено, ожидаем, что поток завершит будущее.

Как только поток выделен для ожидания, он не может быть выгружен, поэтому мы не можем выполнить оставшееся будущее, которое не завершено.

И существует циклическое ожидание, потому что ждет ждет, пока не завершится не завершенное будущее, а другие фьючерсы ждут завершения ожидания.

Просто мы можем сказать, что элемент управления напрямую дойдет до оператора await и начнет ждать завершения незавершенного будущего, которое в любом случае не произойдет. Потому что у нас есть только один поток в нашем пуле потоков.

0 голосов
/ 12 мая 2018

Разве внешняя функция Future при умножении не занимает весь пул потоков (единственный) и, следовательно, не блокируется (поскольку будущее addOne навсегда заблокировано при ожидании потока)?

Да, вроде.

Когда вы звоните val a = addOne(x), вы создаете новое будущее, которое начинает ждать потока. Однако, как вы заметили, единственный поток в настоящее время используется самым отдаленным будущим. Это не было бы проблемой без ожидания, поскольку Futures может справиться с этим условием. Тем не менее, эта строка:

Await.result(result, Duration.Inf)

заставляет внешнее Будущее ждать result Будущее, которое не может работать, потому что внешнее Будущее все еще использует единственный доступный поток. (И, конечно, он также не может работать, потому что фьючерсы a и b не могут работать, опять же из-за внешнего Будущего.)

Вот более простой пример, который также блокирует, не создавая так много Futures:

def addTwo(x: Int) = Future {
  Await.result(addOne(x + 1), Duration.Inf)
}
0 голосов
/ 12 мая 2018
Await.result(result, Duration.Inf)

Когда вы используете await, вы ждете завершения будущего. И вы дали бесконечное время. Поэтому, если так или иначе Future никогда не сможет завершиться, основной поток перейдет в бесконечное ожидание.

...