State Monad有多个线程

时间:2017-04-17 06:16:11

标签: multithreading scala monads state-monad

假设我有一个像

这样的有状态客户端
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)可能会崩溃。

0 个答案:

没有答案