【问题标题】:How to use Tinkerpop with actors如何与演员一起使用 Tinkerpop
【发布时间】:2014-01-20 01:51:16
【问题描述】:

我想知道我是否可以在 Akka Futures 中使用 tinkerpop,到目前为止,当我将更改提交到图表时,它们不会被持久化。 我知道 tinkerpop 是一个线程本地库,这意味着我需要在未来再次设置我的线程ODatabaseRecordThreadLocal.INSTANCE.set(thread)

我尝试了以下方法但没有成功:

def test[T](graphChanges: => T): T = {
    val thread = ODatabaseRecordThreadLocal.INSTANCE.get
    try graphChanges finally {
      ODatabaseRecordThreadLocal.INSTANCE.set(thread)
      GraphPool.get("partitioned").commit
    }
}

// collect tinkerpop frames
test {
  future {
  // add changes to my tinkerpop frames
  }
}

我希望每个 play.mvc.Http.Context 都有 Tinkerpop 线程

这是一个我想要实现的示例项目:https://github.com/D-Roch/tinkerpop-play

【问题讨论】:

    标签: scala playframework-2.0 akka thread-local tinkerpop


    【解决方案1】:

    问题

    问题是,Tinkerpop 在线程本地工作。因此,您的更改仅提交给当前线程。创建 Scala 期货时,您让环境选择未来将在哪个线程中执行。而且环境不知道更好,所以它选择了错误的线程。

    Akka 期货的问题与此类似。

    未来在哪个线程中运行?

    创建未来时,您使用两个参数创建它:

    1. 应该执行的块
    2. 应该执行块的Execution Context

    第二个参数通常作为隐式参数给出。但是你可以覆盖默认值。

    解决方案

    在创建处理 Tinkerpop 的期货时,请使用在同一线程中运行每个块的执行上下文。

    例子:

    import scala.concurrent.ExecutionContext
    import java.util.concurrent.Executors
    
    implicit val ec=ExecutionContext.fromExecutorService(Executors.newSingleThreadExecutor)
    
    future { 
        println(Thread.currentThread); 
        future {
            println(Thread.currentThread)
        }  
    }
    

    此代码在控制台上两次打印出相同的线程 ID(使用 Java 7 和 Scala 2.10.2 测试。

    注意: 使用这么小的线程池很容易导致死锁或饥饿。仅用于您的 Tinkerpop 交互。

    您可以提供一个特殊的方法 tinkerpopFuture,它将块作为参数并返回将在 tinkerpop 线程中运行的未来。或者,您可以创建一个特殊的 actor 来封装所有 tinkerpop 交互(并使用特殊的 tinkerpop 执行上下文运行它们)。

    文学

    【讨论】:

    • 感谢您的意见,这是有道理的,但我真的不知道如何继续,我是否应该在未来之后覆盖 exec 上下文:github.com/D-Roch/tinkerpop-play/blob/master/app/models/…
    • 您使用方法 scala.concurrent.future,它采用“ExecutionContext”类型的隐式第二个参数。在您的情况下,它将从 import ExecutionContext.Implicits.global 中获取默认的 ExecutionContext。您可以通过在单线程执行上下文中提供“隐式 val tinkerpopCtx : ExecutionContext = ...”来覆盖它。有关隐式参数的更多详细信息,请参阅scala-lang.org/old/node/114
    • 谢谢,我了解隐式变量的工作原理,但我会通过什么来覆盖 ExecutionContext ?
    • 您只需使用一个 ExecutionContext 即可在一个线程中运行所有内容。您可以使用 java.util.concurrent.ExecutorService 轻松创建一个,例如ExecutionContext.fromExecutorService(ExecutorService.newSingleThreadExecutor)。我必须尝试正确的语法,然后在上面的答案中添加一个示例。
    • 令人印象深刻,感谢您的解释我没想到答案会这么简单;-) 我做了一些测试,得到了很好的结果,但并不完美:pastebin.com/MsX4R1kE 就像你一样可以看到第 456 行我得到一个失败的插入,但也许它只是 orientdb ?
    【解决方案2】:

    这看起来不像是 Tinkerpop 特有的任何东西,它看起来像是使用 Futures 所犯的常见错误。考虑一下这个片段:

    try graphChanges finally { ... }
    

    它本身看起来不错,但我也可以看到graphChanges 这里正在创造一个未来。所以...

    • graphChanges 启动 Future,立即返回
    • try 块完成,finally 块被执行
    • 在此之前的某个时间点,或之后,或者可能并行,但几乎可以肯定在另一个线程上,Future 被执行

    我的建议是将异步逻辑移到test 中,这样您就可以确定正确的线程关联性并确保任何调用都正确标记为blocking。像这样:

    def test[T](graphChanges: => T): Future[T] = future {
      blocking {
        val tlocal = ODatabaseRecordThreadLocal.INSTANCE
        val dbrecord = tlocal.get
    
        try graphChanges finally {
          tlocal.set(dbrecord)
          GraphPool.get("partitioned").commit
        }
      }
    }
    
    // collect tinkerpop frames
    test {
      // add changes to my tinkerpop frames
    }
    

    【讨论】:

      猜你喜欢
      • 2015-05-19
      • 1970-01-01
      • 2020-10-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-11
      相关资源
      最近更新 更多