将monix.eval.Task转换为scala.concurrent.Future

huangapple go评论91阅读模式
英文:

Converting monix.eval.Task to scala.concurrent.Future

问题

我正在尝试重用一个使用 monix.eval.Task 用于异步任务的模块。
我的项目使用 scala.concurrent.Future

我正在尝试理解将其转换为具有最小性能损失的 Future 的最佳方法。

最简单的方法是使用额外的线程池:

import monix.eval.Task
import monix.execution.Scheduler.Implicits.global

import scala.concurrent.Future

// 创建一个 Monix Task
val monixTask: Task[String] = Task("Hello, Monix!")

// 使用 toFuture 将 Task 转换为 Future
val scalaFuture: Future[String] = monixTask.toFuture

但我并不完全了解性能影响。

  1. 我已经在项目中定义了 scala.concurrent.ExecutionContext。添加全局的 monix.execution.Scheduler 会有什么影响?
  2. 任务将在哪里执行?使用 ExecutionContext 还是 Scheduler?我猜想它们会在 Scheduler 上排队,具有小的转换开销,然后在 ExecutionContext 上运行?

给定的模块只是在所有接收到的 Future 上使用了 Task.deferFuture

这些异步任务是 IO 任务,每秒处理约 300k 到 600k 个请求。

英文:

I’m trying to reuse some module that uses monix.eval.Task for async tasks.
My project uses scala.concurrent.Future.

I'm trying to understand the best way to convert it to Future with the least amount of damage.

The easiest way is to use an additional thread pool:

import monix.eval.Task
import monix.execution.Scheduler.Implicits.global

import scala.concurrent.Future

// Create a Monix Task
val monixTask: Task[String] = Task("Hello, Monix!")

// Convert the Task to a Future using toFuture
val scalaFuture: Future[String] = monixTask.toFuture

But I don't fully understand the performance implications.

  1. I already have scala.concurrent.ExecutionContext defined in the project. What are the implications of adding the global monix.execution.Scheduler?
  2. Where the tasks will actually be computed? With ExecutionContext or Scheduler? I'm guessing it queue up on Scheduler, have a small overhead of conversion, and then run on ExecutionContext?

The given module simply uses Task.deferFuture on all the Future he receives.

The async tasks are IO tasks, having 300k~600k rpm.

答案1

得分: 4

Monix的默认调度器委托给Scala的默认ExecutionContext,因此,如果您想避免使用该EC,您应该定义自己的Scheduler实例,该实例使用您自定义的EC。Scheduler伴生对象定义了一些apply方法,用于此目的。

调度器使用ScheduledExecutorService来_调度_任务,并使用ExecutionContext来_运行_任务。因此,如果将您自定义的ExecutionContext传递给Scheduler.apply,您的任务将在其中运行。

关于TaskFuture之间的互操作的一点说明:Task本质上是惰性的。构造一个Task不会立即运行它。因此,当将返回Future的表达式包装为Task时,deferFuture采用按名称调用的方式,因此直到要求运行该Task时,Future才会被构建和启动。

既然您提到Tasks包含IO(我假设您的意思是"I/O"意义上的,而不是cats.effect.IO),可能适合设置一个单独的专用线程池(ExecutionContext)来执行与IO相关的操作,以避免阻塞您的CPU绑定的"compute"线程。可以使用Scheduler.io作为开始。

英文:

Monix's default Scheduler delegates to the Scala default ExecutionContext, so if you were trying to avoid using that EC, you should define your own Scheduler instance that uses your custom EC. The Scheduler companion has a handful of apply methods defined for this purpose.

A Scheduler uses a ScheduledExecutorService for scheduling tasks, and an ExecutionContext for running the tasks. So if you pass your custom ExecutionContext to the Scheduler.apply, your tasks will run there.

A note about interop between Task and Future: a Task is lazy by nature. Constructing one does not run it. So when wrapping a Future-returning expression as a Task, the deferFuture takes the expression in call-by-name style, so that the Future will not actually be constructed and started until that Task is told to run.

Since you mentioned the Tasks contain IO (I'm assuming you mean in the "I/O" sense, not cats.effect.IO), it might be appropriate to set up a separate dedicated thread pool (ExecutionContext) for performing IO-bound operations, so those don't block your CPU-bound "compute" threads. See Scheduler.io as a starting point for that.

huangapple
  • 本文由 发表于 2023年8月10日 21:50:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/76876383.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定