Есть пара проблем с вашим кодом.Во-первых, как вы указали, между 2-м и 3-м 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
результатов.Это можно исправить, следуя предложению в Могу ли я убедиться, что Haskell выполняет атомарный ввод-вывод? :
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>"]
Это очищает вывод, но все равно не гарантирует, что числа будутпечататься в последовательности, поскольку между разделами atomically
и withMVar
может произойти все, что угодно.Когда я запустил его, как и ожидалось, вывод был в основном по порядку (номера от 1 до 2180), но с некоторыми исключениями.
Может быть способ выполнить инкремент и печать атомарно, но монада STM нене предназначен, чтобы сделать это легко.В частности, см. Все предупреждения об использовании небезопасного ввода-вывода с atomically
.