У меня есть функция, которая возвращает Try
, и я хочу запустить несколько ее экземпляров параллельно, но я не знаю, как это сделать — похоже, я могу запускать ее только один за другим.
Контекст: эта функция предназначена для получения блокировки, чтобы, если несколько потоков/рабочих процессов выполнялись параллельно, они не читали друг друга на пальцах ног. В тестах я хочу запустить пять экземпляров одновременно и утверждаю, что все, кроме одного, были заблокированы. Это работало, когда функция возвращала Future, но я провел некоторый рефакторинг, и теперь она возвращает Try, и тест перестал работать.
Похоже, поведение не связано с кодом блокировки — кажется, я просто не понимаю параллелизм!
Я пытался использовать Future.fromTry
и выполнять их параллельно. Например:
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.util.{Success, Try}
object Main extends App {
def greet(name: String): Try[Unit] = Try {
println(s"Hello $name!")
Thread.sleep(1000)
println(s"Goodbye $name!")
()
}
Seq("alice", "bob", "carol", "dave", "eve").map { name =>
Future.fromTry { greet(name) }
}
}
Я ожидал увидеть все сообщения «Привет», а затем все сообщения «До свидания» — вместо этого, похоже, они выполняются одно за другим.
Hello alice!
Goodbye alice!
Hello bob!
Goodbye bob!
Hello carol!
Goodbye carol!
Hello dave!
Goodbye dave!
Hello eve!
Goodbye eve!
Я осмотрелся и нашел предложения по настройке ExecutionContext и добавлению параллелизма — дело в том, что эта среда, похоже, вполне подходит для параллельного запуска Futures.
На той же машине с тем же глобальным ExecutionContext, если я настрою функцию так, чтобы она возвращала Future, а не Try, я увижу ожидаемый результат, и функции будут работать параллельно.
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.util.{Success, Try}
object Main extends App {
def greet(name: String): Future[Unit] = Future {
println(s"Hello $name!")
Thread.sleep(1000)
println(s"Goodbye $name!")
()
}
Seq("faythe", "grace", "heidi", "ivan", "judy").map { name =>
greet(name)
}
Thread.sleep(2000) // Let the futures finish
}
Hello faythe!
Hello ivan!
Hello grace!
Hello judy!
Hello heidi!
Goodbye ivan!
Goodbye grace!
Goodbye heidi!
Goodbye judy!
Goodbye faythe!
Что я делаю не так с Future.fromTry
, что означает, что он ждет завершения фьючерсов? Как мне сделать так, чтобы он соответствовал второму примеру?
Или я совсем не на то дерево лаю?
Готово и не беспокойтесь, путаница возникает, потому что фьючерсы жаждущий, если бы они были ленивый(например, IO
, Task
или ZIO
), это дало бы ожидаемые результаты. Кстати, если вы считаете, что ваш вопрос может быть слишком простым для SO, вы можете вместо этого попробовать задать его в Гиттер-канал Scala.
документация явно указывает, что fromTry
создаст ужезавершенный Future из результата, поэтому он сначала оценивает функцию, а затем поднимает ее в контексте Future. Таким образом, он полностью серийный.
Вы можете сначала создать List[Future[String]]
из имен, а затем сопоставить список и сопоставить внутренние Futures для выполнения вашей функции.
Или, поскольку Future уже представляет возможную ошибку (и внутренне использует Try), почему бы просто не использовать Future в вашей функции (как вы сказали, что это было раньше).
@LuisMiguelMejíaSuárez Ну, это было проще, чем я думал, теперь я чувствую себя глупо. ? Хочешь опубликовать это как ответ?