我有以下Scotty应用程序:
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Web.Scotty
import Data.Monoid (mconcat)
import Control.Concurrent.STM
import Control.Monad.IO.Class
import Control.Concurrent
main :: IO ()
main = do
counter <- newTVarIO 0
scotty 3000 $
get "/:word" $ do
liftIO $ threadDelay 1000000
liftIO $ atomically $ do
counter' <- readTVar counter
writeTVar counter (counter' + 1)
liftIO $ do
counter' <- atomically (readTVar counter)
print counter'
beam <- param "word"
html $ mconcat ["<h1>Scotty, ", beam, " me up!</h1>"]
并且正在这样调用暴露的端点(200个并发请求):
wrk -c 200 -t 20 -d 10 http://127.0.0.1:3000/z
我期望counter'
的值将按顺序打印。但是,有些数字丢失了,有些数字重复了(例如147
出现了两次,但146
根本没有出现)。
两个问题:
我认为发生这种情况的唯一方法是第二个liftIO
不一定跟在第三个liftIO
之后。它是否正确?还是对此有另一种解释?
如何在第二个counter'
中打印liftIO
的值?我不确定如何将其放在readTVar
和writeTVar
之间(或之后)。
答案 0 :(得分:2)
您的代码有几个问题。首先,正如您所指出的,在第二个liftIO
和第三个main :: IO ()
main = do
counter <- newTVarIO 0
scotty 3000 $
get "/:word" $ do
-- wrap IO in do-block to avoid repeating liftIO
liftIO $ do
threadDelay 1000000
-- Remember the value instead of reading it again.
value <- atomically $ do
x <- readTVar counter
let x' = x + 1
writeTVar counter x'
return x'
print value
beam <- param "word"
html $ mconcat ["<h1>Scotty, ", beam, " me up!</h1>"]
之间可能会发生任何事情(即在递增计数器和再次读取它之间)。您可以像这样重组代码来解决此问题:
print
这将修复丢失的数字和重复的数字。但是,由于交错的main :: IO ()
main = do
counter <- newTVarIO 0
-- Create a lock for printing.
lock <- newMVar ()
scotty 3000 $
get "/:word" $ do
liftIO $ do
threadDelay 1000000
value <- atomically $ do
x <- readTVar counter
let x' = x + 1
writeTVar counter x'
return x'
-- Hold a lock while printing.
withMVar lock $ \_ -> print value
beam <- param "word"
html $ mconcat ["<h1>Scotty, ", beam, " me up!</h1>"]
结果,输出看起来仍然很混乱。您可以按照Can I ensure that Haskell performs atomic IO?中的建议来解决此问题:
atomically
这将清理输出,但仍不能保证数字将按顺序打印,因为在withMVar
和fn register_callback_to_run_inside_c_thread<F>(callback_f: F)
where
F: FnMut() + Send + UnwindSafe + 'static,
{
unimplemented!();
}
部分之间可能发生任何事情。当我运行它时,正如预期的那样,输出大部分是按顺序排列的(从1到2180),但有一些例外。
也许可以通过原子方式执行增量和打印,但是STM monad并非旨在简化这一过程。特别是,请查看所有warnings about using unsafe IO with atomically
。