Warp / WAI的线程安全状态

时间:2012-05-04 13:28:44

标签: haskell concurrency state haskell-warp haskell-wai

我想编写一个Web服务器,将其状态存储在State monad wai / warp中。像这样:

{-# LANGUAGE OverloadedStrings #-}
import Network.Wai
import Network.Wai.Handler.Warp
import Network.HTTP.Types
import Control.Monad.State
import Data.ByteString.Lazy.Char8

main = run 3000 app

text x = responseLBS
        status200
        [("Content-Type", "text/plain")]
    x

app req = return $ text "Hello World"

app1 req = modify (+1) >>= return . text . pack . show

-- main1 = runStateT (run 3000 app1) 0

当然,评论行不起作用。目的是将计数器存储在状态monad中,并在每个请求上显示其增加的值。

另外,我如何获得线程安全? warp是顺序还是并行运行我的中间件?

状态有哪些选项 - 除了IORef之外我还可以使用这个场景吗?

我知道国家给予了安全,但似乎wai不允许国家。

我只需要一个可以从其他地方调用的简单的单线程RPC。 Haxr包需要一个单独的Web服务器,这是一种过度杀伤力。请参阅Calling Haskell from Node.JS - 它没有任何建议,因此我使用Wai / Warp和Aeson编写了一个简单的服务器。但似乎WAI旨在支持并发实现,因此它使事情变得复杂。

2 个答案:

答案 0 :(得分:5)

如果它位于State monad中,则它在设计上是线程安全的。对共享状态没有可能的并发IO操作。它是线程安全的,或者它不会编译。

如果您在设计中真正并行访问共享状态(即单独的forkIO线程更新全局计数器),那么您需要在{中使用MVarTVar {3}}(或其他一些交易障碍)以确保原子性。

答案 1 :(得分:3)

如果只需一次调用atomicModifyIORef就可以表达与状态的交互,那么您可以使用它,而不需要显式地序列化对状态的访问。

import Data.IORef

main = do state <- newIORef 42
          run 3000 (app' state)

app' :: IORef Int -> Application
app' ref req
   = return . text . pack . show `liftM` atomicModifyIORef ref (\st -> (st + 1, st + 1))

如果您的互动更加复杂,并且您需要强制执行请求的完整序列化,则可以将MVarStateT结合使用。

import Control.Concurrent.MVar
import Control.Monad.State.Strict

main = do state <- newMVar 42
          run 3000 (app' state)

app' :: MVar Int -> Application
app' ref request
   = do state <- takeMVar ref
        (response, newState) <- runStateT (application request) state
        putMVar newState --TODO: ensure putMVar happens even if an exception is thrown
        return response

application :: Request -> StateT Int (ResourceT IO) Response
application request = modify (+1) >>= return . text . pack . show