【问题标题】:scala: could we yield/resume actor just like goroutine?scala:我们可以像 goroutine 一样让 Actor/Resume 恢复吗?
【发布时间】:2016-05-26 14:04:45
【问题描述】:

我正在学习 scala。我对actor和goroutine的区别感到很困惑。

众所周知,golang的并发单元是goroutine,并发模型在我看来是非常特殊和优雅的:

  • CPU 绑定任务

运行时调度程序会尽最大努力抢占长时间运行的协程,例如函数调用时检查抢占,让长时间运行的协程不会占用系统线程太久

  • 事件驱动调用

例如,在 Linux 上,可以通过 epoll 管理的所有类型的事件,例如socket connect,将产生 goroutine 并在稍后调用完成时恢复它。当 goroutine 屈服时,持有线程将被释放以处理下一个准备好的 goroutine

  • cgo 调用

此类调用将独占一个系统线程,直到调用返回。

如何在 scala actor 中处理所有这些类型的调用?

scala 重用并依赖 JDK 来访问低级系统调用,对吗?例如网络套接字。一些高级库也在 scala 中被重用,例如JDBC。 Java 的并发模型是线程(系统线程),Java 中的所有调用都会阻塞线程,直到调用返回。那么如何将它们封装在actor中呢? Actor 的表现和 goroutine 有什么不同吗?

期待任何专业的答案!谢谢!

编辑:

我搜索了一些文档,我猜答案可能是:

  • CPU 绑定任务

scala 中没有提供抢占

  • 事件驱动调用

由于 Akka 已集成到 scala 中,所以要么使用 Akka 异步包装库(例如 Akka TCP),要么使用 future?

  • cgo 调用

???

我更改了这篇文章的标题。我已经了解actor和goroutine的风格区别,我只关心actor中如何处理不同类型的调用,就像gorotuine一样。

编辑:

对不起,我终于知道我的问题到底是什么了:

我们可以像 goroutine 一样让出/恢复演员吗?

或者演员只是一个应该从头到尾运行的计划计算部分?即不能中断整个消息处理。

而且由于Java中的线程模型,我认为它缺乏对基于协程的阻塞调用的底层支持,对吧?例如socket read,如果阻塞,yield协程,当有新数据读到读时,恢复协程;虽然它对程序员是透明的,但您只需要编写 socket.read()。众所周知,在Java中,整个过程都是基于系统线程的,就像C一样是传统的方式。

【问题讨论】:

标签: scala actor


【解决方案1】:

如果您只想知道如何处理 IO 调用,Future 在 scala 中被认为更惯用。

当你有一个层次模型,或者你想要任何复杂的故障恢复机制时,Akka 是很好的,但如果你想做的只是 IO,Future 就足够了,而且简单得多。

另外,IO 调用可以是阻塞或非阻塞(在线程级别),您需要单独处理。幸运的是,它就像

一样简单
Future {
  // non-blocking IO call
} 

import scala.concurrent.blocking
Future {
  blocking {
    ... some blocking IO call
  }
}

如果需要做大量的IO阻塞调用,使用单独的ExecutionContext

并且不要将 CPU 绑定任务包装在 Future 中,CPU 绑定意味着您的程序不会因为 Future 而受益。如果需要 Future 匹配接口,使用

Future.successful {
  // computation
}

我从

那里学到了大部分这些细节
  1. https://github.com/alexandru/scala-best-practices/blob/master/sections/4-concurrency-parallelism.md
  2. http://docs.scala-lang.org/overviews/core/futures.html

编辑1:问题已更新,此编辑尝试回答新问题

:我们可以让出/恢复演员吗?

A: 我不太确定我是否理解正确,请问您为什么要让步并恢复演员? 您似乎希望控制运行时的低级处理细节,我认为这是actor无法实现的

Actor 是响应式的,这意味着每当您向其发送消息时它都会执行某些操作,仅此而已,您无法控制事情的执行方式,同一个 Actor 甚至可能在不同的线程上运行。

通常如果我们需要异步操作,我们会将操作包装在Future 中,但是future 不会自动使问题消失,如果你包装在Future 中的操作阻塞了线程,它仍然会阻塞,但是包裹in Future 使您的代码异步,即下一行代码可以运行而无需等待上一个操作返回。

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import java.lang.Thread

Future {
  Thread.sleep(1000)
  println("Woke up")
}

println("Sleeping")

结果是

Sleeping
Woke up

【讨论】:

  • 您好,清伟,您不能将阻塞{}-block 放在Future.apply 调用周围,因为Future.apply 的块将在另一个线程上执行。 (你必须在 Future.apply 的 inside 中调用它。
  • 谢谢@Qingwei。在 golang 中,所有阻塞 IO 调用都与 goroutine 耦合,这意味着当您在 goroutine 中编写顺序代码时,可能会涉及由 epoll 管理的异步操作和多次自动 yield/resume,但幕后的一切对程序员,他们甚至不需要关心线程的东西。现在我很清楚演员/未来无法做到这一点。在 scala 中,你仍然需要关心线程、阻塞,并在必要时使用 java NIO API。
猜你喜欢
  • 2019-06-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-08
  • 2012-07-01
  • 1970-01-01
  • 2020-10-26
  • 2011-11-16
相关资源
最近更新 更多