多线程 – 播放Scala和线程安全

该项目使用Play框架和Scala语言编写.
我已经实现了编译时依赖性.
我在Play中关注了这个例子:

https://github.com/playframework/play-scala-compile-di-example

看看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实例.
只要应用程序存在,该实例就会存在.这些陈述是否正确?

我的应用程序中的HomeController看起来像这样:

class HomeController{

   val request = // some code here

   val workflowExecutionResult = Workflow.execute(request)

}

因此,工作流是类型对象而不是类.

工作流程看起来像这样:

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

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

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

    workflowExecutionResult
  }
}

因此,Workflow调用一些域服务,每个域服务都是object类型而不是类.
域服务中的所有值都是不可变的,我到处都在使用val.

这足以确保线程安全吗?

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

class HomeControllerInSeeSharpProject{

    // some code here

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

并且工作流程看起来像这样:

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
  }
}

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

情况可能如此吗?我的应用程序不是线程安全吗?

我已经读过Scala中的对象不是线程安全的,因为它们只有一个实例.不过我也读过这个
它们不是线程安全的使用val将使应用程序线程安全…

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

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

实际上,HomeController表现得像一个单身人士,避免单身人士可能更安全.例如,默认情况下,Play Framework使用Guice依赖注入,只要它不是@Singleton,就会为每个请求创建一个新的控制器实例.一个动机是,如Nio’s answer所示,关于并发保护的状态较少:

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和线程安全 - 代码日志