为什么Akka对并发有好处?


9

我是Akka和actor框架的新手-我确信我缺少明显的东西,请提前接受我的道歉。

我一直在读,选择Akka的主要要点之一是它管理并发的方式。

我不清楚Akka为什么如此特别。我知道有很多小演员非常轻快。但是,当两个用户同时保存一个表单时,这对我有什么帮助?

我是否还需要某种并发锁(悲观/乐观/等等)?


您是在问为什么演员对并发或特别是Akka有利吗?
scriptin

Wouldn't I still need some sort of concurrency lock (pessimistic/optimistic/etc..)?-是的,但这是基础RDBMS的责任,而不是参与者。Akka演员很可能没有直接与您的用户交谈。相反,Akka参与者和用户(本质上是另一个参与者)都直接与数据库进行交互。
罗伯特·哈维

Answers:


7

诸如参与者和代理之类的消息处理模型的优点之一是,传统的并发问题(主要是共享状态的同步)不再是问题。actor可以保留私有状态,并可以不加锁地自由更新它。actor框架可确保一次仅处理一条消息。通过序列化处理,可以以无锁方式编写代码。

在用户保存表单的示例中,假设参与者正在保留每个表单中的一些数据的列表,则参与者可以在不加锁的情况下更新列表,因为该框架保证一次只能处理一个表单。传统上,您将必须锁定列表访问权限或使用并发列表。

并发策略是一个稍微不同的问题,仍然是您的责任(没有策略是最常见的策略)。为了稍微更改您的示例,假设两个用户都尝试同时更新SAME表单实例。如果没有并发策略,一个人的更改将覆盖另一个(可能是最后一个获胜)。没关系,但是充其量只能导致更改被覆盖的用户无法预期的行为。如果他们查看刚刚更改的表单,它将具有意外的值(来自其他用户)。在最坏的情况下(当我们不仅在讨论表单更新时,还在讨论运输订单之类的事情时)可能会导致各种损失(时间,收益等)。

使用并发策略有助于识别这些情况,并能够根据业务规则解决它们。例如,Optimistic Concurrency使用户发送要更新的表单的版本。当参与者执行更改时,它会注意到第二位用户认为该表单正在更新版本5,因为该表单实际上是版本6,因为第一个用户进行了更新。现在至少我们可以通知第二个用户,自他们开始编辑表格以来,该表格已经更改。或企业要在此处执行的任何规则。

在更新表单的情况下,您可能不太在乎并发(我想这取决于情况)。但是在其他情况下,至少能够检查和处理违规行为可能是非常重要的事情。您甚至可能要忽略并发冲突,例如如果用户更改了不同的部分(以继续表格类推)。或者,如果更改对业务有重大影响(大订单),则您要接受该更改并在以后解决小冲突(例如,年度联系信息更新尚未完成)。

我相信Akka还有很多其他方面,例如如何处理故障,主管等等,这些都是开发人员的重要考虑因素。


9

我将与其他并发模型(例如经典的基于锁的并发和简洁的事务性内存)进行比较,来大致介绍Actor模型(不仅仅是Akka)。

优点

  1. 更容易理解和使用的概念

    • 基于锁的并发是困难的;特别是很难正确地做到这一点,因为有很多概念需要理解和使用才能正确和有效:锁,信号量,线程,同步,互斥,内存屏障等。

    • 另一方面,演员是一个更抽象的概念。您有发送和接收消息的演员。无需掌握和使用诸如内存屏障之类的低级概念。

  2. 增强不变性

    • 可变性是编程中许多错误和错误的根源,尤其是在多线程应用程序中。Actor模型通过强制不变性解决了这个问题。
  3. 更少的错误倾向

    • 由于上述两个原因
  4. 高效的

    • 效率不如基于好的书面锁有效,但通常比软件事务性存储器更有效。
  5. 轻松扩展

    • 至少从理论上讲,该应用程序应该通过添加更多参与者来执行您的工作来很好地扩展。基于锁的扩展非常困难。

缺点

  1. 并非所有语言都容易实现不变性。

    • Erlang是最早普及角色的语言,其核心具有不变性,但是Java和Scala(实际上是JVM)没有强制不变性。
  2. 还是很复杂

    • Actor基于异步编程模型,该模型不是那么简单,并且在所有情况下都易于建模。处理错误和失败情况特别困难。
  3. 不防止死锁或饥饿

    • 两个参与者可以处于等待对方的消息的状态。因此,与锁一样,您也有死锁,尽管调试起来容易得多。但是,有了事务性内存,就可以保证没有死锁。
  4. 不太有效

    • 由于强制不变性,并且由于许多参与者必须使用相同的线程进行切换,因此参与者不会像基于锁的并发那样高效。

结论

基于锁的并发是最有效的,但是它很难编程并且容易出错。软件事务性内存最清晰,易于编程且出错率较低,但效率最低。参与者在这两个模型之间的各个方面都在各个方面:易于编程,效率高和易于出错。


对于您的第二个优势,难道不是“ 可变性是许多错误的源...”和“ ...通过禁止可变性 ”而不是“ 不变性 ” 解决了这个问题?另外,第二句话有两个多余的提到解决问题的内容。
84:00

@ 8bittree是的,您是对的;我拼错了。万一您不知道,可以编辑答案以更正此类问题。
m3th0dman

我知道这一点,我只是不愿进行更改,以免某件事对我造成误解,从而使某件事的含义发生颠倒或彻底的改变。
8bittree

您能详细说明一下僵局吗?似乎您必须构造一个非常尴尬的actor设置(双向通信)才能遇到该问题。如果您使用的是询问方式的模式(A要求B回答,B处理请求并回复请求发送者,但从未直接联系A),我看不出有什么可能的僵局
Daenyth

1
@Daenyth我同意你必须使用一个怪异的构造来实现与演员的僵局。但是从类似的角度来看,您必须使用怪异的构造来实现基于锁的并发的死锁(尽管我同意,使用互斥锁创建死锁比使用actor容易得多)。无论如何,这个想法是,无论选择哪种怪异的构造,只有事务性内存可以确保您不会出现死锁。
m3th0dman 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.