玩 Scala 和线程安全 [英] Play Scala and thread safety
问题描述
该项目使用Play framework
和Scala
语言编写.我已经实现了编译时依赖
.我从 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)
}
所以 Workflow
是 object
类型而不是 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
.域服务中的所有值都是不可变的,我到处都使用 val
s.
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 val
s 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
不是线程安全的,因为它们只有一个实例.但是我也读过,虽然它们不是线程安全的,使用 val
s 将使应用程序线程安全...
I have read that object
s 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 val
s 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屋!