有关并行性和`do`的问题

时间:2019-04-11 11:54:01

标签: haskell parallel-processing stm

我有以下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根本没有出现)。

两个问题:

  1. 我认为发生这种情况的唯一方法是第二个liftIO不一定跟在第三个liftIO之后。它是否正确?还是对此有另一种解释?

  2. 如何在第二个counter'中打印liftIO的值?我不确定如何将其放在readTVarwriteTVar之间(或之后)。

1 个答案:

答案 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

这将清理输出,但仍不能保证数字将按顺序打印,因为在withMVarfn 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

相关问题