Блокирующий контекст в Scala не очень хорошо подходит для смешанных блокирующих / неблокирующих заданий. Зачем? - PullRequest
5 голосов
/ 26 марта 2019

Я пытаюсь разобраться в конструкции blocking. Хотя не совсем понятно, как это работает внутри, общая идея, которую я получил, заключалась в том, что, пока я использую глобальный пул потоков Scala, обертывание моего кода в контекст blocking будет гарантировать, что пул потоков создаст для этого дополнительное пространство. задание (поскольку оно не связано с процессором).

  (1 to 1000).foreach { i =>
    Future {
      println(i)
      Thread.sleep(100 * 1000)
    }
  }

быстро покажет, что одновременно могут выполняться только 8 заданий, а

  (1 to 1000).foreach { i =>
    Future {
        blocking {
            println(i)
            Thread.sleep(100 * 1000)
        }
    }
  }

покажет, что сейчас у нас около 250 одновременных заданий. Вот Это Да! Что меня тогда застало врасплох, так это то, что

  (1 to 1000).foreach { i =>
    Future {
      println(i)
      Thread.sleep(100 * 1000)
    }
  }

  ('a' to 'z').foreach { c =>
    Future {
        blocking {
            println(c)
            Thread.sleep(100 * 1000)
        }
    }
  }

снова покажет только 8 одновременных заданий - блокирующие задания не будут выполнены сразу.

Почему это? Какова на самом деле внутренняя механика контекста blocking?

Ответы [ 2 ]

4 голосов
/ 27 марта 2019

blocking вступает в силу только после того, как вы вошли в контекст блокировки. Поскольку у вас запущено 8 неблокирующих фьючерсов, новые фьючерсы не будут запущены, поэтому они не смогут войти в контекст блокировки. Другими словами, Scala не «знает», что они блокируют, пока они не начнут выполняться.

Вы можете думать, что второй фрагмент работает так:

  1. Первое будущее создано и начато.
  2. Первое будущее сигнализирует о том, что оно блокируется с помощью вызова blocking, поэтому реализация освобождает место для дальнейшего развития.
  3. Тем временем в главном потоке создается и запускается второе будущее.
  4. ...

В то время как ваш последний фрагмент работает так:

  1. Первое будущее создано и начато. Это не означает, что он блокируется.
  2. Второе будущее делает то же самое.
  3. ...
  4. Девятое будущее создано, но не запущено, так как есть 8 неблокирующих фьючерсов.
  5. ...
  6. 1001-е будущее (первое во втором цикле) создано, но не запущено, поскольку существует 8 неблокирующих фьючерсов. Поскольку он не запущен, у него никогда не будет возможности сообщить реализации, что он блокируется, путем вызова blocking.
0 голосов
/ 27 марта 2019

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

Не уверен, что именно это "застало тебя врасплох". Как только первый вызов foreach завершится, он перейдет ко второму и начнется еще через 26.

...