更新:请记住,我刚刚开始学习Haskell
让我们说我们正在编写一个具有以下一般功能的应用程序:
现在,如果应用程序是用Java编写的,我们可以使用静态ConcurrentHashMap对象来存储数据(代表Java类)。因此,在启动期间,应用程序可以使用数据填充地图,然后servlet可以访问它,为客户端提供一些API。
如果应用程序是用Erlang编写的,我们可以使用ETS / DETS存储数据(作为本机Erlang结构)。
现在的问题是:实施此类设计的Haskell方式是什么? 它不应该是DB,它应该是某种轻量级的内存存储器,可以存储复杂的结构(Haskell本机结构),并且可以从不同的线程(servlet,Java-world实体交谈)访问)。在Haskell中:没有像Java中那样的静态全局变量,没有Erlang中的ETS和OTP,所以如何以正确的方式完成(没有使用像Redis这样的外部解决方案)?
由于
更新:问题的另一个重要部分 - 因为Haskell没有(?)拥有全局静态'变量,然后实现这个全球可访问的正确方法是什么?数据保持对象(比如,它是" stm-containers")?我应该在“主要”的某个地方初始化它吗?函数然后只是将它传递给每个REST API处理程序?或者还有其他更正确的方法吗?
答案 0 :(得分:8)
从您的问题中不清楚客户端API是否会提供变更数据的方法。
如果不是(即,API只是关于查询),那么任何不可变的数据结构就足够了,因为不可变数据的一个美妙之处在于它可以安全地从多个线程访问,并确保它可以&# 39;改变。不需要锁的开销或其他策略来处理并发。您只需在初始化期间构造不可变数据,然后只查询它。为此,请考虑像"unordered-containers"这样的包。
如果您的API也将改变数据,那么您将需要可变数据结构,这些结构针对并发进行了优化。 "stm-containers"是一个包,提供这些包。
答案 1 :(得分:5)
首先,我假设你的意思是它需要可用于多个线程,而不是多个进程。 (区别在于线程共享内存,进程没有。)如果这个假设是错误的,那么你的大部分问题都没有意义。
所以,第一个重点:Haskell具有可变数据结构。它们可以在线程之间轻松共享。这是一个小例子:
import Control.Concurrent
import Control.Monad
main :: IO ()
main = do
v <- newMVar 0 :: IO (MVar Int)
forkIO . forever $ do
x <- takeMVar v
putMVar v $! x + 1
forM_ [1..10] $ \_ -> do
x <- readMVar v
threadDelay 100
print x
注意在MVar
中放置值时使用($!)。 MVar
不强制要求评估其内容。确保一切正常运行有一些微妙之处。在了解Haskell的评估模型之前,您将获得大量空间泄漏。这就是为什么这种事情通常在一个处理所有细节的库中完成的部分原因。
鉴于此,第一步方法是在MVar
中存储某种地图。除非它受到很多争论,否则它实际上具有相当不错的性能。
当它处于争用状态时,你有一个很好的后备辅助方法,特别是在使用哈希映射时。这是条纹。不是在一个MVar中存储一个地图,而是在N个MVar中使用N个地图。查找的第一步是使用哈希来确定要查看的N个MV中的哪个。
有一些花哨的无锁算法,可以使用更细粒度的可变值来实现。但总的来说,它们需要大量的工程工作才能在性能上提高几个百分点,这在大多数用例中并不重要。