1

下面是一个示例程序的源代码:

当我从 ghci 运行它时, printJob 和 printJob2 都运行良好并将十行写入文本文件。

但是当使用 -threaded 标志编译时,程序只写一行。

我在 ArchLinux 上有 ghc 7.0.3

这是编译命令:

ghc -threaded -Wall -O2 -rtsopts -with-rtsopts=-N -o testmvar testmvar.hs

我在做什么错?为什么它在线程模式下不起作用?

import Control.Concurrent.MVar
import Control.Concurrent (forkIO)
import Control.Exception (bracket)
import Control.Monad (forM_)
import System.IO.Unsafe (unsafePerformIO)
import System.IO (hPutStrLn, stderr)


{-# NOINLINE p #-}
p :: MVar Int
p = unsafePerformIO $ newMVar (1::Int)


{-# NOINLINE printJob #-}
printJob x = bracket (takeMVar p) (putMVar p . (+ 1))
                   (\a -> do
                       appendFile "mvarlog.txt" $ "Input: " ++ x ++ "; Counter: " ++ show a ++ "\n"
                       )


{-# NOINLINE printJob2 #-}
printJob2 = unsafePerformIO $ do
   p2 <- newEmptyMVar
   return $ (\x -> bracket (putMVar p2 True) (\_ -> takeMVar p2)
                   (\_ -> do
                       appendFile "mvarlog.txt" $ "preformed " ++ x ++ "\n"
                   ))

main = do
  forM_ [1..10]
    (\x -> forkIO $ printJob (show x))

编辑:hammar 指出,如果主应用程序早于所有产生的线程退出,那么它们将被杀死并建议在 main 结束时添加延迟。我做到了,正如他预测的那样,它有效。

4

2 回答 2

5

问题是你的主线程完成得太快了,当 Haskell 程序的主线程完成时,所有其他线程都会自动终止。根据线程的调度方式,这可能发生在任何线程有机会运行之前。

一个快速而肮脏的解决方案是简单地threadDelay在 末尾添加 a main,尽管更健壮的方法是使用像 an 这样的同步原语MVar来指示主线程何时可以完成。

例如:

main = do
  vars <- forM [1..10] $ \x -> do
    done <- newEmptyMVar -- Each thread gets an MVar to signal when it's done
    forkIO $ printJob (show x) >> putMVar done ()
    return done

  -- Wait for all threads to finish before exiting
  mapM_ takeMVar vars
于 2012-02-26T00:46:55.150 回答
0

当然,它不起作用。使用 unsafePerformIO 总是会困扰你。构造您的代码以不使用它。使用它来创建全局变量不是合法使用它。这就是 reader monad 的用途。Haskell 中的任何内容都不需要 unsafePerformIO。

当人们推荐这个“技巧”时,它显然被打破了,这让我很生气。就像盲人引导盲人一样。只是不要这样做,使用 Haskell 不会有问题。Haskell 对你提出的每一个问题都有非常漂亮和优雅的解决方案,但是如果你坚持与之抗争而不是学习它,那么你将总是遇到错误。

于 2012-02-26T22:53:42.583 回答