【发布时间】:2011-12-19 15:39:09
【问题描述】:
我从事 Java 开发已经很多年了,但是在我开始进行 Android 开发之前,我从来没有过多地处理并发问题,并且突然开始发现“应用程序没有响应”和明显的死锁情况。
这让我意识到理解和调试其中一些并发问题是多么困难。 Scala 和 Go 等新语言如何提高并发性?它们如何更易于理解以及如何防止并发错误?有人可以提供展示优势的真实示例吗?
【问题讨论】:
标签: scala concurrency go
我从事 Java 开发已经很多年了,但是在我开始进行 Android 开发之前,我从来没有过多地处理并发问题,并且突然开始发现“应用程序没有响应”和明显的死锁情况。
这让我意识到理解和调试其中一些并发问题是多么困难。 Scala 和 Go 等新语言如何提高并发性?它们如何更易于理解以及如何防止并发错误?有人可以提供展示优势的真实示例吗?
【问题讨论】:
标签: scala concurrency go
简化并发的三个主要竞争者是参与者、软件事务内存 (STM) 和自动并行化。 Scala 实现了所有这三个。
Actor 在 Erlang 语言中找到了他们最显着的实现,据我所知,这就是这个想法的开始*。 Erlang 是围绕演员从头开始设计的。这个想法是演员本身就是彼此的黑匣子。它们仅通过传递消息进行交互。
Scala 在其库中实现了actor,并且在外部库中提供了变体。在主库中,不强制执行黑盒性,但有易于使用的传递消息的方法,并且 Scala 可以轻松创建不可变消息(因此您不必担心发送带有一些内容的消息,然后在某个随机时间更改内容。
actor 的优点是您不必担心复杂的共享状态,这确实简化了所涉及的推理。此外,您可以将问题分解为比线程更小的部分,并让参与者库找出如何将参与者捆绑到适当数量的线程中。
缺点是,如果您尝试做一些复杂的事情,在您知道它成功之前,您需要处理很多逻辑来发送消息、处理错误等。
STM 基于这样的思想,即最重要的并发操作是获取一些共享状态,对其进行修改,然后将其写回。所以它提供了一种方法来做到这一点;但是,如果它遇到一些问题——它通常会延迟检测到最后,此时它会检查以确保写入全部正确——它会回滚更改并返回失败(或重试)。
这既是高性能(在只有适度争用的情况下,因为通常一切都很好)并且对大多数类型的锁定错误具有鲁棒性,因为 STM系统可以检测到问题(甚至可能会做一些事情,例如从较低优先级的请求中获取访问权并将其提供给较高优先级的请求)。
与演员不同,尝试复杂的事情更容易,只要你能处理失败。但是,您还必须正确推理底层状态; STM 通过失败和重试来防止罕见的意外死锁,但如果你只是犯了一个逻辑错误并且某些步骤无法完成,STM 不允许它这样做。
Scala 有一个 STM 库,它不是标准库的一部分,但正在考虑纳入。 Clojure 和 Haskell 都有完善的 STM 库。
自动并行化采取你不想考虑并发的观点;你只想让事情快速发生。因此,如果您有某种并行操作——例如,一次将一些复杂的操作应用于项目集合,并因此产生一些其他集合——您应该具有自动并行执行此操作的例程。 Scala 的集合可以以这种方式使用(有一个.par 方法可以将传统的串行集合转换为其并行模拟)。许多其他语言具有类似的功能(Clojure、Matlab 等)。
编辑:实际上,Actor model 早在 1973 年就已被描述,可能是受 Simula 67 早期工作的启发(使用协程而不是并发); 1978 年出现了相关的Communicating Sequential Processes。因此,Erlang 的功能在当时并不是独一无二的,但该语言在部署 Actor 模型方面是独一无二的。
【讨论】:
对我来说,与传统并发模型相比,使用 Scala (Akka) Actor 有几个优势:
您仍然需要对并发和多线程编程有充分的了解,因为仍然可能出现死锁和竞争条件,但是参与者可以更轻松地识别和解决这些问题。我不知道这些对 Android 应用有多大影响,但我主要从事服务器端编程,而使用 Actor 使开发变得更加容易。
【讨论】:
在惯用的 Go 程序中,线程通过通道传递状态和数据。 这可以在不需要锁的情况下完成(通道仍然使用引擎盖下的锁定)。通过通道将数据传递给接收者,意味着数据所有权的转移。一旦你通过通道发送了一个值,你就不应该再对它进行操作,因为现在接收它的人“拥有”它。
但是,应该注意的是,Go 运行时不会以任何方式强制执行这种“所有权”转移。通过通道发送的对象不会被标记或标记或类似的东西。这只是一个约定。因此,如果您愿意,您可以通过改变您之前通过通道发送的值来打自己的脚。
Go 的优势在于 Go 提供的语法(启动 goroutine 和通道的工作方式)使编写正确运行的代码变得容易得多,从而防止竞争条件和死锁。 Go 清晰的并发机制使您可以很容易地推断程序中将要发生的事情。
附带说明:如果您真的想使用 Go 中的标准库,它们仍然提供传统的互斥锁和信号量。但您显然是自行决定并承担风险。
【讨论】:
Scala Actor 的工作原理是无共享的,因此没有锁(因此也没有死锁)! Actor 侦听消息,并由包含 Actor 可以处理的内容的代码调用。
【讨论】: