6

我们正在使用 Scala、Play 框架和 MongoDB(使用 ReactiveMongo 作为我们的驱动程序)构建一个 Web 应用程序。应用程序架构是非阻塞端到端的。

在我们代码的某些部分,我们需要访问一些非线程安全的库,例如 Scala 的解析器组合器、Scala 的反射等。我们目前将此类调用封装在synchronized块中。我有两个问题:

  1. synchronized与未来代码一起使用时有什么需要注意的问题吗?
  2. 从性能和可用性的角度来看,使用锁(例如ReentrantLock)比使用锁更好吗?synchronized
4

4 回答 4

8

这是一个老问题))参见这里using-actors-instead-of-synchronized例如。简而言之,使用演员而不是锁会更可取:

class GreetingActor extends Actor with ActorLogging {

  def receive = {
    case Greeting(who) ⇒ log.info("Hello " + who) 
  }
}

在任何给定时间只会处理一条消息,因此您可以放置​​任何您想要的非线程安全代码而不是 log.info,一切都会正常工作。顺便说一句,使用询问模式,您可以将您的演员无缝集成到需要期货的现有代码中。

于 2013-07-24T07:00:51.363 回答
3

对我来说,您将面临的主要问题是任何对同步或锁定代码部分的调用都可能阻塞,从而使执行上下文的线程瘫痪。为避免此问题,您可以使用以下方法包装对潜在阻塞方法的任何调用scala.concurrent.blocking

import scala.concurrent._
import ExecutionContext.Implicits.global

def synchronizedMethod(s: String) = synchronized{ s.size }

val f = future{ 
   println("Hello")
   val i = blocking{  //Adjust the execution context behavior
     synchronizedMethod("Hello")
   }
   println(i)
   i
 }

当然,最好考虑诸如线程局部变量之类的替代方案,或者将调用包装到actor内部的串行代码中。

最后,我建议使用同步而不是锁。对于大多数应用程序(特别是如果关键部分很大),性能差异并不明显。

于 2013-07-24T07:23:12.300 回答
2

好吧,我认为最简单和最安全的方法是(如果可以的话)来自Thread Confinement。即每个线程创建自己的解析器组合器等实例,然后使用它。

如果您需要任何同步(应该避免,因为在流量下它将成为杀手),synchornized或者ReentrantLock会提供几乎相同的性能。它再次取决于哪些对象需要保护哪些锁等。在 Web 应用程序中,除非绝对必要,否则不鼓励这样做。

于 2013-07-24T07:27:50.570 回答
2

你提到的例子,即反射和解析应该是相当不可变的,你不应该需要锁定,但如果你要使用锁,那么同步块就可以了。我认为使用同步与锁定之间没有太大的性能差异。

于 2013-07-24T07:00:04.883 回答