英文:
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
但我并不完全了解性能影响。
- 我已经在项目中定义了
scala.concurrent.ExecutionContext
。添加全局的monix.execution.Scheduler
会有什么影响? - 任务将在哪里执行?使用
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.
- I already have
scala.concurrent.ExecutionContext
defined in the project. What are the implications of adding the globalmonix.execution.Scheduler
? - Where the tasks will actually be computed? With
ExecutionContext
orScheduler
? I'm guessing it queue up onScheduler
, have a small overhead of conversion, and then run onExecutionContext
?
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
,您的任务将在其中运行。
关于Task
和Future
之间的互操作的一点说明: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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论