Scala Cats Effects - IO Async Shift - Как это работает? - PullRequest
0 голосов
/ 21 сентября 2018

Вот некоторый код для кошек Scala, использующий IO Monad :

import java.util.concurrent.{ExecutorService, Executors}

import cats.effect.IO

import scala.concurrent.{ExecutionContext, ExecutionContextExecutor}
import scala.util.control.NonFatal

object Program extends App {

  type CallbackType = (Either[Throwable, Unit]) => Unit

  // IO.async[Unit] is like a Future that returns Unit on completion.
  // Unlike a regular Future, it doesn't start to run until unsafeRunSync is called.
  def forkAsync(toRun: () => Unit)(executor: ExecutorService): IO[Unit] = IO.async[Unit] { callback: CallbackType =>
    // "callback" is a function that either takes a throwable (Left) or whatever toRun returns (Right).
    println("LalalaAsync: " + Thread.currentThread().getName)
    executor.execute(new Runnable {
      def run(): Unit = {
        val nothing: Unit = toRun() // Note: This line executes the body and returns nothing, which is of type Unit.
        try {
          callback(Right(nothing)) // On success, the callback returns nothing
        } catch {
          case NonFatal(t) => callback(Left(t)) // On failure, it returns an exception
        }
      }
    })
  }

  def forkSync(toRun: () => Unit)(executor: ExecutorService): IO[Unit] = IO.apply {
    println("LalalaSync: " + Thread.currentThread().getName)
    executor.execute(new Runnable {
      def run(): Unit = {
        toRun()
      }
    })
  }

  val treadPool: ExecutorService = Executors.newSingleThreadExecutor()
  val mainThread: Thread = Thread.currentThread()

  val Global: ExecutionContextExecutor = ExecutionContext.global

  /*
  Output:
    1 Hello World printed synchronously from Main.main
    LalalaSync: scala-execution-context-global-12
    Hello World printed synchronously from thread pool.pool-1-thread-1
    LalalaAsync: scala-execution-context-global-12
    Hello World printed asynchronously from thread pool.pool-1-thread-1
    2 Hello World printed synchronously from Global .scala-execution-context-global-12
   */
  val program = for {
    _ <- IO {
      println("1 Hello World printed synchronously from Main." + Thread.currentThread().getName) // "main" thread
    }
    _ <- IO.shift(Global) // Shift to Global Execution Context
    _ <- forkSync { () =>
      println("Hello World printed synchronously from thread pool." + Thread.currentThread().getName) // "pool-1-thread-1" thread
    }(treadPool)
    _ <- forkAsync { () =>
      println("Hello World printed asynchronously from thread pool." + Thread.currentThread().getName) // "pool-1-thread-1" thread
    }(treadPool)
    _ <- IO.shift(Global) // Shift to Global Execution Context
    _ <- IO {
      println("2 Hello World printed synchronously from Global ." + Thread.currentThread().getName) // "scala-execution-context-global-13" thread
    }
  } yield ()

  program.unsafeRunSync()
}

Чтобы запустить его, вам нужно добавить:

libraryDependencies ++= Seq(
  "org.typelevel" %% "cats" % "0.9.0",
  "org.typelevel" %% "cats-effect" % "0.3"
),

В вашу сборку.sbt файл.

Обратите внимание на вывод:

  /*
  Output:
    1 Hello World printed synchronously from Main.main
    LalalaSync: scala-execution-context-global-12
    Hello World printed synchronously from thread pool.pool-1-thread-1
    LalalaAsync: scala-execution-context-global-12
    Hello World printed asynchronously from thread pool.pool-1-thread-1
    2 Hello World printed synchronously from Global .scala-execution-context-global-12
 */

По сути, я не понимаю, как работает IO.shift (Global) или как IO.async.

Например, почему после того, как я вызываю «forkAsync», если я не вызываю «IO.shift (Global)», последующие синхронные объекты ввода-вывода запускаются в «pool-1-thread-1».Кроме того, в чем разница между forkAsync и forkSync в этом примере?Оба они запускаются в ExecutionContext.global, а затем исполняют Runnable в "pool.pool-1-thread-1".

Например, forkAsync и forkSync делают одно и то же, или forkAsync делает что-то другое?Если они делают то же самое, какой смысл заключать код в IO.async?Если они не делают одно и то же, чем они отличаются?

1 Ответ

0 голосов
/ 21 сентября 2018

Например, почему после того, как я вызываю "forkAsync", если я не вызываю "IO.shift (Global)", последующие синхронные объекты ввода-вывода запускаются в "pool-1-thread-1 ".

Более важный вопрос: почему вы ожидаете, что он оценит" последующие объекты синхронного ввода-вывода "в глобальном?

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

Обновление до последней версии 1.0.0 и у вас также есть evalOn в ContextShift, который выполнит действие IO в указанном пуле потоков и затем вернется к вашему «глобальному», что, как я полагаю, вам и нужно.

Кроме того, в чем разница между forkAsync и forkSync в этом примере?

Ваш forkSync запускает выполнение Runnable, но не ожидает его завершения.Это огонь и забудь.Это означает, что последующие цепные действия не будут оказывать обратного давления.

Несколько советов:

  1. обновить до последней версии (1.0.0)
  2. прочитать документыпо адресу: https://typelevel.org/cats-effect/datatypes/io.html
...