玩 Scala 和线程安全 [英] Play Scala and thread safety

查看:75
本文介绍了玩 Scala 和线程安全的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

该项目使用Play frameworkScala 语言编写.我已经实现了编译时依赖.我从 Play 中遵循了这个例子:

The project is written using Play framework and Scala language. I have implemented compile time dependency. I have followed this example from Play:

https://github.com/playframework/play-scala-compile-例子

查看MyApplicationLoader.scala:

import play.api._
import play.api.routing.Router

class MyApplicationLoader extends ApplicationLoader {
  private var components: MyComponents = _

  def load(context: ApplicationLoader.Context): Application = {
    components = new MyComponents(context)
    components.application
  }
}

class MyComponents(context: ApplicationLoader.Context) 
  extends BuiltInComponentsFromContext(context)
  with play.filters.HttpFiltersComponents
  with _root_.controllers.AssetsComponents {

  lazy val homeController = new _root_.controllers.HomeController(controllerComponents)

  lazy val router: Router = new _root_.router.Routes(httpErrorHandler, homeController, assets)
}

以及以下代码行:

 lazy val homeController = new _root_.controllers.HomeController(controllerComponents)

我的理解是只有一个 HomeController 实例是在第一次调用 HomeController 时创建的.只要应用程序存在,该实例就会存在.这些说法正确吗?

my understanding is that there is only one instance of HomeController created the first time HomeController is called. And that instance lives as long as the application lives. Are these statements correct?

我的应用程序中的 HomeController 如下所示:

The HomeController in my application looks like that:

class HomeController{

   val request = // some code here

   val workflowExecutionResult = Workflow.execute(request)

}

所以 Workflowobject 类型而不是 class.

So Workflow is of type object and not class.

Workflow 看起来像这样:

object Workflow {
  def execute(request: Request) = {

    val retrieveCustomersResult = RetrieveCustomers.retrieve() 
    // some code here

    val createRequestResult = CreateRequest.create(request)
    // some code here

    workflowExecutionResult
  }
}

所以Workflow 调用了一些域服务,每个域服务的类型都是object 而不是class.域服务中的所有值都是不可变的,我到处都使用 vals.

So Workflow calls a few domain services and each domain service is of type object and not class. All values inside the domain services are immutable, I am using vals everywhere.

这足以确保线程安全吗?

Is this enough to ensure thread safety?

我问,因为我习惯于编写 C# Web API,其中 HomeController 看起来像这样:

I am asking as I'm used to writing C# Web APIs where a HomeController would look like that:

class HomeControllerInSeeSharpProject{

    // some code here

    var request = new Request() // more code here
    var workflow = new WorkflowInSeeSharpProject()
    var workflowExecutionResult = workflow.execute(request)
}

Workflow 看起来像这样:

public class WorkflowInSeeSharpProject {

  public execute(Request request) {

      var retrieveCustomers = new RetrieveCustomers()
      var retrieveCustomersResult = retrieveCustomers.retrieve()

      // some code here
      var createRequest = new CreateRequest()
      var createRequestResult = createRequest.create(request)

      // some code here
      return workflowExecutionResult
  }
}

因此,在 C# 项目中,每次 HomeControllerInSeeSharpProject 被调用时,都会创建一个 WorkflowInSeeSharpProject 的新实例并创建所有域服务也是新的,然后我可以确定状态不能在单独的线程之间共享.所以恐怕是因为我的 Scala Workflow并且域服务的类型是 object 而不是 class 可能会出现两个请求被发送到 HomeController 的情况并且状态在这两个线程之间共享.

So in a C# project every time a HomeControllerInSeeSharpProject is called a new instance of WorkflowInSeeSharpProject is created and all the domain services are also newed-up and then I can be sure that state cannot be shared between separate threads. So I am afraid that because my Scala Workflow and domain services are of type object and not class that there could be a situation where two requests are sent into the HomeController and state is shared between those two threads.

可以这样吗?我的应用程序不是线程安全的吗?

Can this be the case? Is my application not thread safe?

我已经读到 Scala 中的 object 不是线程安全的,因为它们只有一个实例.但是我也读过,虽然它们不是线程安全的,使用 vals 将使应用程序线程安全...

I have read that objects in Scala are not thread safe since there is only single instance of them. However I have also read that although they are not thread safe using vals will make the application thread safe...

或者Play本身有办法解决这个问题吗?

Or maybe Play itself has a way to deal with that problem?

推荐答案

因为您使用的是编译时依赖注入,您可以控制创建的实例数量,而在您的情况下 HomeController 只创建一次.当请求进来时,这个单个实例将在线程之间共享,因此您确实必须确保它是线程安全的.HomeController 的所有依赖项也需要是线程安全的,因此 object Workflow 必须是线程安全的.目前,Workflow 没有公开暴露任何共享状态,因此它是线程安全的.通常,object 中的 val 定义是 线程安全.

Because your are using compile time dependency injection, you control the number of instances created, and in your case HomeController is created only once. As requests come in, this single instance will be shared between threads so indeed you have to make sure it is thread-safe. All the dependencies of HomeController will also need to be thread-safe, thus object Workflow has to be thread-safe. Currently, Workflow is not publicly exposing any shared state, so it is thread-safe. In general, val definitions within object are thread-safe.

实际上 HomeController 表现得像一个单例,避免单例可能更安全.例如,默认情况下,Play Framework 使用 Guice 依赖注入,它会为每个控制器创建一个新的控制器实例请求,只要它不是 @Singleton.一个动机是,正如 蔚来的回答:

In effect HomeController is behaving like a singleton and avoiding singletons could be safer. For example, by default Play Framework uses Guice dependency injection which creates a new controller instance per request as long as it is not a @Singleton. One motivation is there is less state to worry about regarding concurrency protection as suggested by Nio's answer:

一般来说,最好不要使用@Singleton,除非你有对不变性和线程安全的公平理解.如果你认为你有一个单例用例,但要确保你是保护任何共享状态.

In general, it is probably best to not use @Singleton unless you have a fair understanding of immutability and thread-safety. If you think you have a use case for Singleton though just make sure you are protecting any shared state.

这篇关于玩 Scala 和线程安全的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆