Multithreading &引用;持有;内存中的数据映射

Multithreading &引用;持有;内存中的数据映射,multithreading,haskell,memory-management,haskell-snap-framework,Multithreading,Haskell,Memory Management,Haskell Snap Framework,我定义了三种数据结构,其中S、LL、M和Object,分别表示Set、ListLike、Map,以及ByteString: nouns :: IO [Object] nouns = liftM LL.words $ B.readFile "nounlist.txt" obj :: IO ObjectSet obj = liftM S.fromList nouns actions :: IO ActionMap actions = do n <- nouns let l = f

我定义了三种数据结构,其中
S
LL
M
Object
,分别表示
Set
ListLike
Map
,以及
ByteString

nouns :: IO [Object]
nouns = liftM LL.words $ B.readFile "nounlist.txt"

obj :: IO ObjectSet
obj =  liftM S.fromList nouns

actions :: IO ActionMap
actions = do
  n <- nouns
  let l = foldl' (\z x -> (x,Sell):(x,Create):z) [] n
  return $ M.fromList $
    (\(x,y) -> ((x, Verb y []), Out (Verb y []) x)) <$> l
请记住,我的Map由300000多个键值对组成:在我的计算机上,当调用第一个查询时,第一次求值的初始时间开销大约在3-5秒之间;这是完全可以预料的。接下来的每一个电话都是快速响应的,这正是我想要的。然而,之所以如此,是因为我将此代码作为一个独立的可执行文件来运行,并且有幸停留在
进程的
IO()
中。如果我要将此代码(以及未列出的其他附带代码)转换为一个库,以便与say.接口。。作为一个Snap框架Web应用程序,我不一定会有这种奢侈。本质上,我想说的是:如果我要从
过程中永远删除
,那么经过评估的映射和集合肯定会被垃圾收集。实际上,当我从Snap应用程序调用函数时会发生这种情况(我不能永远保持
,因为它会阻止Snap应用程序)。来自Snap应用程序的每个后续调用都会有相同的3-5秒开销,因为它会重新评估所讨论的数据结构。
我的问题:

是否有一种简单的方法来保存地图并在内存中设置,以便后续的每次查找都很快?我想到的一个想法是运行一个线程,用于睡眠和维护地图和场景的存储。然而,对我来说,这绝对是矫枉过正。我忽略了什么?谢谢你接受我冗长的解释


注意:我不一定在寻找代码答案、更多建议、建议等。我相信
TVar
可以做到这一点

import Control.Concurrent.Concurrent

intial=do
    objects <- newTVarIO Nothing
    --I didn't understand your example code well, bear with me (and fix this.)
    queryMachine <- mkQueryMachine objects
    return queryMachine

queryMachine objects=QueryMachine $ do
    objects''' <- atomically $ do
        objects' <- readTVar objects
        case objects' of
            Nothing -> do
                let objects'' = objectsMaker
                writeTVar objects $ Just objects''
                return objects''
            Just objects'' -> return objects''

    profitFrom objects'''
导入控制.Concurrent.Concurrent
初始的
对象返回对象“”
从对象“”中获利
适应你自己的需要

解释


TVar是STM单子中的可变变量。STM是线程安全的。
以原子方式将其转换为IO操作。对于要执行此操作的多种类型的操作,创建单独的STM操作,并对每种操作进行原子调用。这是因为您希望
原子地
块很小,这样它们所使用的锁就不会占用太多的“锁”(某种程度上)。可以通过从Control.Concurrent.STM.TSem获得一个TSem来改进上述代码,这将确保只有一个线程会尝试计算它,以防两个请求同时出现。

以下是我对
IORef
的想法:

import Data.IORef
import System.IO.Unsafe 
import Control.Monad 

val_ :: IORef (Maybe Integer)
val_ = unsafePerformIO $ newIORef Nothing

val :: IO Integer
val = do 
  v <- readIORef val_
  case v of 
    Just v' -> return v' 
    Nothing -> do
           v' <- readFile "large.txt" 
           -- replace this part with your actual computation
           let l = sum $ map (fromIntegral . fromEnum) v' 
           writeIORef val_ $ Just l
           return l 

main = do 
  writeFile "large.txt" (replicate (10^7) '0')
  putStrLn "reading"
  replicateM_ 10 (val >>= print)
import Data.IORef
导入System.IO不安全
进口管制
val_uu2;::IORef(可能是整数)
val_uuz=unsafePerformIO$newIORef Nothing
val::IO整数
val=do
v do

v'在snaplet初始化期间,您只能评估一次
obj
actions
,并将结果存储到snaplet的状态

data SnapApp = SnapApp
    { objectSet :: ObjectSet
    , actionMap :: ActionMap
    }

appInit :: SnapletInit SnapApp SnapApp
appInit = makeSnaplet ... $ do
    ... 
    a <- liftIO actions
    o <- liftIO obj
    return $ SnapApp o a

这保证了
actions
obj
只会被评估一次。

使用顶级
IORef
来存储
actions
怎么样?@user2407038 TBH,我以前从未使用过
IORef
。如果您建议这是一种解决我的内存问题的方法,那么我将仔细阅读这个主题。我不熟悉
Snap
,但是为什么不在应用程序初始化期间评估
名词
obj
,然后将这些(纯)值传递给
进程::[Object]->[ObjectSet]->IO()
。这将确保这些值只被评估一次。@cdk将对此进行研究,我已经在
Snap
端进行了类似的试验,不幸的是,我也正在学习Snap\=。感谢您的建议。snap是否为您重复运行它?我们将尽快查看您的并发解决方案。谢谢你的输入@PyRulez。实际上我还没有试过这个;有没有办法不用线程就能做到这一点?如上所述,我正在考虑一种线程类型的解决方案,但我认为这种解决方案过于简单,存储对象过于简单。我只是想确保在获得线程快乐之前,我尝试了所有可能的合理解决方案。虽然我不想解决这个问题,但我确实提出了线程,您也告诉了我一些与线程相关的重要方面+1我认为线程继承到web服务器。这不是forks的代码。你是对的,这对web服务器很重要。您的伪代码中似乎没有调用任何
forkIO
,这也是正确的。我想我被导入的
并发
库有点分心,但是是的,你在技术上是对的@PyRulez,对此很抱歉。你的回答显然是最精辟的,而且效果很好。几乎不需要重建,我能够在Snap的框架内工作。性能恢复,顺便说一下,你有一个非常酷的头像。多谢各位+1、请核对,谢谢。关于《阿凡达》,它是由瓦西里·康定斯基的一些画作制成的:不是我真正想象的解决问题的方式;但是,您确实告诉了我一些IORef特性以及它们如何与GC一起工作。无论如何,这是我很好奇的事情+1.
data SnapApp = SnapApp
    { objectSet :: ObjectSet
    , actionMap :: ActionMap
    }

appInit :: SnapletInit SnapApp SnapApp
appInit = makeSnaplet ... $ do
    ... 
    a <- liftIO actions
    o <- liftIO obj
    return $ SnapApp o a
someUrlHandler :: Handler SnapApp SnapApp
someUrlHandler = do
  a <- gets actionMap
  o <- gets objectMap
  res <- query a o
  ...