scala - 在实践中消息传递并发语言如何优于共享内存并发语言

标签 scala concurrency go

我从事 Java 开发已经很多年了,但是在我开始进行 Android 开发之前,我从来没有过多地处理并发问题,并且突然开始发现“应用程序没有响应”和明显的死锁情况。

这让我意识到理解和调试其中一些并发问题是多么困难。 Scala 和 Go 等新语言如何提高并发性?它们如何更易于理解以及如何防止并发错误?有人可以提供展示优势的真实示例吗?

最佳答案

简化并发的三个主要竞争者是参与者、软件事务内存 (STM) 和自动并行化。 Scala 实现了所有这三个。

Actor

Actor 在 Erlang 语言中找到了他们最显着的实现,据我所知,这就是这个想法的开始*。 Erlang 是围绕 Actor 从头开始设计的。这个想法是 Actor 本身就是彼此的黑匣子。它们仅通过传递消息进行交互。

Scala 在其库中实现了actor,并且在外部库中提供了变体。在主库中,不强制执行黑盒性,但有易于使用的方法来传递消息,并且 Scala 可以轻松创建不可变消息(因此您不必担心发送带有一些内容的消息,然后在某个随机时间更改内容。

actor 的优点是您不必担心复杂的共享状态,这确实简化了所涉及的推理。此外,您可以将问题分解为比线程更小的部分,并让参与者库找出如何将参与者捆绑到适当数量的线程中。

缺点是,如果您尝试做一些复杂的事情,在您知道它成功之前,您需要处理很多逻辑来发送消息、处理错误等。

软件事务内存

STM 基于这样的思想,即最重要的并发操作是获取一些共享状态,对其进行修改,然后将其写回。所以它提供了一种方法来做到这一点;但是,如果它遇到一些问题——它通常会延迟检测到最后,此时它会检查以确保写入全部正确——它会回滚更改并返回失败(或重试)。

这既是高性能(在只有适度争用的情况下,因为通常一切都很好)并且对大多数类型的锁定错误具有鲁棒性,因为 STM系统可以检测到问题(甚至可能会做一些事情,比如从较低优先级的请求中获取访问权并将其提供给较高优先级的请求)。

与 Actor 不同,尝试复杂的事情更容易,只要你能处理失败。但是,您还必须正确推理底层状态; STM 通过失败和重试来防止罕见的意外死锁,但如果你只是犯了一个逻辑错误并且某些步骤无法完成,STM 不允许它这样做。

Scala 有一个 STM 库,它不是标准库的一部分,但正在考虑纳入。 Clojure 和 Haskell 都有完善的 STM 库。

自动并行化

自动并行化采取你不想考虑并发的观点;你只想让事情快速发生。因此,如果您有某种并行操作——例如,一次对一组项目应用一些复杂的操作,并因此产生一些其他集合——您应该具有自动并行执行此操作的例程。 Scala 的集合可以以这种方式使用(有一个 .par 方法可以将传统的串行集合转换为其并行模拟)。许多其他语言具有类似的功能(Clojure、Matlab 等)。


编辑:实际上,Actor model早在 1973 年就被描述过,可能是受到 Simula 67 早期工作的启发(使用协程而不是并发); 1978 年出现了相关的 Communicating Sequential Processes .因此,Erlang 的功能在当时并不是独一无二的,但该语言在部署 Actor 模型方面是独一无二的。

关于scala - 在实践中消息传递并发语言如何优于共享内存并发语言,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7969557/

相关文章:

java - 使用 groovy actor 最大化数据库的吞吐量?

git - Cygwin 对环境变量使用相对和绝对路径

go - 如果异常断开,Websocket 会卡住

json - 结构字段中的大写字母

scala - 在 Scala 文档中搜索#::

scala - 将 List[Any] 转换为 List[Int]

java - 线程限制

java - 跨线程变量的每次读取都应该是 volatile 的、原子性的还是由同步包装的?

scala - 如何在 sbt 或 Scala 的 REPL 中获得类似 jdb 的功能(设置断点或显示变量)?

scala - Scala Slick 3.0 中的随机行