假设我有一个像
这样的有状态客户端trait Client {
case class S(connection: Connection, failures: Int) // Connection takes 10 seconds to create
def fetchData: State[S, Data] // Takes 10 seconds to fetch
}
我想让它变得有状态,因为连接的创建很昂贵(所以我想要缓存它)+我有失败计数信号,如果连续存在太多故障,那么基本上重新创建连接。
我从State monad得到的理解是,它应该在一个线程上有效地计算,在该线程上按顺序链接不可变状态。在这种情况下我负担不起,因为我获取操作需要花费大量时间,而我所需要的只是快速读取状态,使用那里的连接来启动昂贵的异步调用。另一方面,我无法使其State[S, Task[Data]]
,因为如果任务failures
失败,我需要修改S
中的fetchData
。所以我修改了客户端
import fs2.Task
trait Client {
case class S(connection: Connection, failures: Int) // Connection takes 10 seconds to create
def fetchData: StateT[Task, S, Data] // Takes 10 seconds to fetch
}
问题是我可以提出一些Monoid,如果初始状态是什么,可以添加无关的状态。例如。如果我有S1 -> (S2, Data)
,S1 -> (S3, Data)
,我仍然可以添加S2和S3以达到最终状态,无论之前是什么 - S2或S3。
现在我有一种使用处理程序的Java服务器(Thrift)。我还没有深入研究它如何正常工作的细节,但我们假设它侦听传入的连接,然后生成一个处理数据检索的线程。所以在我的情况下,我想阻止线程,直到Task
完成:
class Server(client: Client) {
def handle: Data {
run(client.fetchData)
}
private var state: S = Monoid[S].mempty
private def run(st: State[S, Data]): Data {
val resTask: Task[(S, Data)] = st.run(state)
val (newState, res) = Await.result(resTask.unsafeRunAsyncFuture)
state.synchonized({
state = Monoid[S].mappend(state, newState)
})
res
}
}
我的问题是:
1)我在这里做的是正确/最好的方式(我的重点是效率/没有错误)
2)我真的需要State monad,或者最好使用普通var并使用它吗?
3)在State monad的例子中,我总是看到状态被传播到应用程序的顶层并在那里处理。但也许最好在Client
级别处理它并将无状态接口暴露给应用程序?
4)这段代码线程是否安全,或者我应该使某种状态为某种同步var?我知道在Objective C中,如果在赋值过程中从另一个线程读取状态,则此行state = Monoid[S].mappend(state, newState)
可能会崩溃。