线程应用程序中的unsafePerformIO不起作用

时间:2012-02-25 23:54:19

标签: multithreading haskell unsafe-perform-io

以下是示例程序的来源:

当我从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结束时添加延迟。 我做了,正如他预测的那样,它有效。

2 个答案:

答案 0 :(得分:5)

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

快速而肮脏的解决方案是在threadDelay末尾添加main,但更强大的方法是使用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

答案 1 :(得分:-1)

当然,它不起作用。使用unsafePerformIO总会回来困扰你。构建代码以不使用它。使用它来创建全局变量不是它的合法用法。这就是读者monad的用途。在Haskell中你不需要unsafePerformIO。

当人们在明显打破时推荐这个“技巧”时,它会让我失望。就像盲人领导盲人一样。只是不要这样做,你就不会有使用Haskell的问题。 Haskell为你抛出的每一个问题都提供了非常漂亮和优雅的解决方案,但是如果你坚持打击而不是学习它,那么你将永远遇到错误。