【问题标题】:Using Actors instead of `synchronized`使用 Actors 而不是 `synchronized`
【发布时间】:2012-01-03 04:36:26
【问题描述】:

每次我读到在 Scala 中使用 synchronized 时,作者通常会提到应该使用 Actors 来代替(例如 this)。虽然我大致了解演员是如何工作的,但我真的很想在一段代码中看到一个演员被用来替换 Java 的 synchronized 方法修饰符(我的意思是它的 Scala 等效项 - synchronized 块)的示例。例如,修改数据结构的内部结构会很好看。

这是对 Actor 的良好使用还是我被误导了?

【问题讨论】:

    标签: java scala programming-languages concurrency actor


    【解决方案1】:

    1) 概述

    Scala Actors 可以替换标准 Java 线程应用程序中的复杂业务逻辑,这通常会避开开发复杂多线程系统的开发人员。

    考虑一下在一个简单的线程应用程序中可能会看到的以下 java 代码 sn-p(此代码正在等待异步请求完成)。

    myAsyncRequest.startCalculation(); 
    while(notDone)
       myAsyncRequest.checkIfDone();
       Thread.sleep(1000); 
    System.out.println("Done ! Value is : " + myAsyncRequest.getCalculationValue());
    

    要查看使用 Scala 的更高级别并发模型直接替换此类代码,请查看此帖子:Scala program exiting before the execution and completion of all Scala Actor messages being sent. How to stop this?

    2) 现在:回到代码 snpipet --- 这里有一些明显的问题,让我们快速看一下:

    • 代码将“监控”计算执行的逻辑与计算结果的处理相结合。
    • 代码中嵌入了启发式算法 (Thread.sleep(1000)),没有明确的逻辑理由(为什么要等一秒?为什么不等 3 秒?),从而在代码块中添加了不必要的逻辑。
    • 它无法扩展 - 如果我正在运行 1000 个客户端,并且每个客户端都在不断检查结果,我可能会产生一些非常丑陋的流量 --- 没有充分的理由。

    scala 如何修改这个范例?

    • Scala 演员可以返回“期货”

    这些概括了您希望演员做的“事情”很快就会完成的期望。 scala“未来”替换了这个java构造:它使“明确”的事实是,我的while循环“期望”在不久的将来会发生一些事情,并且之后有一个动作要做。

    • Scala 演员可以传递“消息”

    虽然我正在“等待”(在上面的 while 循环中)完成,但很明显,另一种实现方式是计算对象在完成时简单地“告诉我”。消息传递可以实现这一点,但有些复杂,并且在某些 java 实现中会导致无法追踪、无法阅读的代码。由于 scala 以直接设计为适应并发工作负载的方式抽象了这个概念,消息传递设计模式现在可以以不太复杂的方式实现,从而将“等待”逻辑与逻辑解耦的处理。

    3) 简短的回答:一般来说,Scala API 的构建是为了在更高抽象级别对并发逻辑进行编码,因此您的并发代码是声明性的,而不是在实现细节上混乱.

    4) 同步:一个较低级别的概念,虽然必不可少,但会使我们的代码复杂化。

    同步是低级多线程编程的产物。通过提供最常见的并行编程范式的更高级别的抽象,Scala 使这种特殊结构在许多最常见的并发编程用户案例中变得不必要。事实上,如今,甚至 java 也这样做了 :) java.util.concurrent 包为我们提供了原子数据类型和数据结构,避免了将简单操作包装在“同步”块中的需要。但是,标准 Java 不支持“Actor”和“Futures”的高级概念,这些概念可以有效地管理和协调,而无需手动管理同步方法调用或对象修改。

    【讨论】:

    • 这是对 Actor 的一个很好的解释,但并没有真正讨论他们取消 synchronized 关键字的能力。
    • 触摸'!我在最后添加了一个部分。我认为常见的 java“同步”习语并没有完全被 scala 取代——它只是 Scala 对并发工作流有面向对象的包装器,这使得使用较低级别的并发构造变得不太必要......另外,请记住在 Scala 中,您可以调用 synchronized() 方法来同步整个对象。
    【解决方案2】:

    Actor 保证一次只处理一条消息,这样就不会有两个线程访问任何实例成员 - 因此无需使用同步

    【讨论】:

    • 这里我有点好奇。由于actor实际上便于并发计算。假设我有一个列表来收集硬盘中的所有文件名。我想使用演员范式来实现这一点。我的电脑有 4 个核心,我想演员应该允许我的程序使用所有这 4 个核心来扫描文件并将名称放入我的列表中。如果没有同步,actor如何保证一次只处理一个“add”?
    • 在这种情况下,您可能有 2 种类型的 actor 一种扫描目录并发出带有要添加的名称的消息,另一种接受此类消息并将其添加到列表中(以及提供用于检索列表的消息。您将拥有第一个池并在它们之间分配工作和第二个的单个实例。请注意,这是一个简单的场景,因此复杂性可能不值得。在现实世界中实现正确同步的工作更难
    • 嗯……这个案子有两种类型的演员是有意义的。而同步实际上是由第二个参与者的消息队列(邮箱?)处理的,对吗?
    • 是的。 Actor系统负责将消息同步到邮箱中
    猜你喜欢
    • 2011-08-10
    • 1970-01-01
    • 2016-11-23
    • 1970-01-01
    • 1970-01-01
    • 2012-05-17
    • 2011-06-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多