Actor的值有时会返回null

时间:2013-12-18 17:59:08

标签: multithreading scala akka future

我有一个演员和其他一些对象:

object Config {
  val readValueFromConfig() = { //....}
}

class MyActor extends Actor {

 val confValue = Config.readValueFromConfig()

 val initValue = Future {
   val a = confValue // sometimes it's null
   val a = Config.readValueFromConfig() //always works well 
 }

 //..........

}

上面的代码是我实际拥有的非常简化的版本。奇怪的是,有时val a = confValue会返回null,而如果我将其替换为val a = Config.readValueFromConfig(),那么它总能正常运作。

我想知道,这是因为与演员互动的唯一方式是发送消息吗?因此,由于val confValue不是局部变量,我必须使用val a = Config.readValueFromConfig()(不同的对象,不是演员)或val a = self ! GetConfigValue并在之后阅读结果?

2 个答案:

答案 0 :(得分:2)

  

我想知道,这是因为与演员互动的唯一方式是发送消息吗?因此,由于val confValue不是局部变量,我必须使用val a = Config.readValueFromConfig()(一个不同的对象,而不是一个actor)

仅仅因为它不是演员,并不意味着它必然是安全的。可能不是。

  

或val a = self! GetConfigValue并在之后读取结果?

这几乎是正确的。您的意思是self ? GetConfigValue,我认为 - 会返回Future,然后您可以map结束。 !不返回任何内容。

您无法直接在Future内读取actor的变量,因为(通常)Future可以在任何线程上,任何处理器核心上运行,并且您没有任何内存屏障那里强制CPU缓存从主存储器重新加载值。

答案 1 :(得分:2)

val readValueFromConfig() = { //....}

这给了我一个编译错误。我假设你的意思是没有括号?

val readValueFromConfig = { //....}

相同的逻辑与不同的时间给出不同的结果=竞争条件。

  • val confValue = Config.readValueFromConfig()始终在构造MyActor个对象期间执行(因为它是MyActor的一个字段)。有时这会返回null。
  • val a = Config.readValueFromConfig() //always works well总是稍后执行 - 构建MyActor之后,当Future initValueExecutor执行时。似乎这永远不会返回null。

可能的原因:

  1. 如果readValueFromConfig的身体依赖于另一个人,可以解释一下 并行/异步操作已完成。您是否有机会异步读取配置?鉴于此方法的名称,它可能只是从文件同步读取 - 这意味着这不是原因。
  2. Singleton对象不是线程安全的吗?我编译了你的代码。这是你的单例对象java类的反编译:

    public final class Config
    {
      public static String readValueFromConfig()
      {
        return Config..MODULE$.readValueFromConfig();
      }
    }
    
    public final class Config$
    {
      public static final  MODULE$;
      private final String readValueFromConfig;
    
      static
      {
        new ();
      }
    
      public String readValueFromConfig()
      {
        return this.readValueFromConfig;
      }
    
      private Config$()
      {
        MODULE$ = this;
        this.readValueFromConfig = // ... your logic here;
      }
    }
    

    Mmmkay ......除非我弄错了,否则这不是线程安全的。

    IF 两个线程正在访问readValueFromConfig(比如说​​Thread1首先访问它),然后在方法private Config$()内,MODULE$this.readValueFromConfig之前被不安全地发布设置(引用this过早地转义构造函数)。紧跟在后面的Thread2可以在设置之前读取MODULE$.readValueFromConfig。如果'... your logic here'很慢并阻塞线程,那么极有可能成为问题 - 这正是同步I / O所做的。

    故事的道德:避免来自Actors(或任何Threads,包括Executors)的有状态单例对象,或者通过非常谨慎的编码风格使它们变得线程安全。解决方法:更改为def,在内部缓存私有val中的值。