我已经是一名Java开发人员多年了,但直到开始进行Android开发并突然遇到“应用无响应”和明显的死锁情况之前,从未涉及过太多并发问题。
这让我认识到理解和调试某些并发问题有多么困难。新的语言如Scala和Go如何改进并发?它们如何更易于理解并如何预防并发错误?能否提供真实世界的例子来说明这些优势?
我已经是一名Java开发人员多年了,但直到开始进行Android开发并突然遇到“应用无响应”和明显的死锁情况之前,从未涉及过太多并发问题。
这让我认识到理解和调试某些并发问题有多么困难。新的语言如Scala和Go如何改进并发?它们如何更易于理解并如何预防并发错误?能否提供真实世界的例子来说明这些优势?
简化并发的三种主要方法是actors、软件事务内存(STM)和自动并行化。Scala 实现了这三种方法。
Actors 最著名的实现是在 Erlang 语言中,据我所知这个想法就是从那里开始的。Erlang 是围绕着 actors 设计的。这个想法是让 actors 相互之间成为黑盒子;它们只通过传递消息来交互。
Scala 在其库中实现了 actors,而在外部库中也有一些变体可用。在主要库中,并没有强制要求使用黑盒子,但是有易于使用的方法来传递消息,而 Scala 使创建不可变消息变得容易(因此你不必担心在某个随机时间发送带有某些内容的消息,然后更改内容)。
actors 的优点是您无需担心复杂的共享状态,从而简化涉及的推理。此外,您可以将问题分解为比线程更小的片段,并让 actor 库确定如何将 actors 打包到适当数量的线程中。
缺点是如果您尝试做某些复杂的操作,则需要处理很多逻辑,例如发送消息、处理错误等,直到您确认它成功为止。
STM 基于这样的想法:最重要的并发操作是获取一些共享状态,对其进行修改,然后将其写回。因此,它提供了一种方法来执行此操作;但是,如果它遇到问题-通常延迟检测到最后一刻-然后检查以确保所有写入都正确执行,否则就会回滚更改并返回失败(或重试)。
这既具有高性能(在只有适度争用的情况下,因为通常一切都很顺利),又对大多数锁定错误具有鲁棒性,因为 STM 系统可以检测问题(甚至可能执行诸如从低优先级请求中取出访问权并将其给予高优先级请求等操作)。
与演员不同的是,只要你能处理失败,尝试复杂的事情就更容易。但是,您还必须正确推理底层状态;STM通过失败和重试来防止罕见的意外死锁,但如果您只是犯了一个逻辑错误,某些步骤无法完成,STM也无法允许它。
Scala有一个STM库,它不是标准库的一部分,但正在考虑将其包含进来。Clojure和Haskell都有成熟的STM库。
自动并行化的想法是,您不希望考虑并发性;您只想让东西快速发生。因此,如果您有某种并行操作--例如对集合中的每个项目应用某些复杂的操作,并产生另一组集合作为结果--则应该有自动执行此操作的例程。Scala的集合可以以这种方式使用(有一个.par
方法,可以将传统的串行集合转换为其并行模拟)。许多其他语言也具有类似的功能(Clojure、Matlab等)。
编辑:实际上,Actor模型早在1973年就被描述过了,可能是受到了Simula 67中早期使用协程而不是并发的影响;1978年出现了相关的通信顺序进程。因此,Erlang的能力当时并不是独一无二的,但该语言在部署Actor模型方面具有独特的效果。
尽管死锁和竞争条件仍然可能存在,但您仍需要对并发和多线程编程有相当的了解,但actors使得识别和解决这些问题更加容易。我不知道这些在Android应用程序中有多少适用性,但我大多数时间都是做服务器端编程,使用actors使开发变得更加容易。
Scala actors 的工作原理是基于 shared-nothing 原则的,因此没有锁(也就不会有死锁)!Actors 监听消息并被调用来处理某个 actor 需要处理的代码。