【问题标题】:Thread-safe state with Warp/WAIWarp/WAI 的线程安全状态
【发布时间】:2012-05-04 13:28:44
【问题描述】:

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

{-# 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

当然,注释行不起作用。目的是将计数器存储在 state monad 中,并在每次请求时显示其递增的值。

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

该州有哪些可用选项 - 除了IORef 我可以在这种情况下使用什么?

我知道 State 提供安全,但似乎 wai 不允许 State。

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

【问题讨论】:

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


    【解决方案1】:

    如果它在State monad 中,它在设计上是线程安全的。没有可能的共享状态的并发 IO 操作。要么是线程安全的,要么无法编译。

    如果作为设计的一部分,您可以真正并行访问共享状态(即单独的 forkIO 线程更新全局计数器),那么您需要在 STM monad(或其他一些事务性障碍)以确保原子性。

    【讨论】:

    • 如果可能,我想避免真正并行访问共享状态。我想连续处理请求。
    【解决方案2】:

    如果您与状态的交互可以通过对 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
    

    【讨论】:

    • 第一个给Precedence parsing error cannot mix '.' [infixr 9] and 'liftM' [infixl 9] in the same infix expression。第二个报告了 StateTrunStateT 的模棱两可的用法; import Control.Monad.State被评论后,报告Not in scope: type constructor or class 'ResourceT';在importing Control.Monad.Trans.Resource 之后,它报告Couldn't match expected type 'ResourceT IO t0'
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-14
    • 2011-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多