unsafePerformIO в многопоточных приложениях не работает - PullRequest
0 голосов
/ 26 февраля 2012

Ниже приведен источник примера программы:

Когда я запускаю его из ghci, и printJob, и printJob2 работают нормально и записывают десять строк в текстовый файл.

Но при компиляции с -флаг с резьбой, программа пишет только одну строку.

У меня ghc 7.0.3 на ArchLinux

Вот команда компиляции:

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))

РЕДАКТИРОВАТЬ: Хаммар указал, что если основное приложение завершит работу раньше, чем все порожденные потоки, то они будут уничтожены и предложил добавить задержку в конце основного,Я сделал, и, как он предсказал, это работает.

Ответы [ 2 ]

5 голосов
/ 26 февраля 2012

Проблема в том, что ваш основной поток завершает работу слишком рано, и когда основной поток программы на 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
0 голосов
/ 27 февраля 2012

Конечно, это не работает.Использование unsafePerformIO всегда дает о себе знать.Структурируйте свой код так, чтобы он не использовался.Использование его для создания глобальных переменных не является законным использованием.Вот для чего читательская монада.Вам не нужно unsafePerformIO для чего-либо в Haskell.

Меня убивает, когда люди рекомендуют этот «трюк», когда он явно сломан.Это как слепой ведущий слепого.Только не делайте этого, и у вас не будет проблем с использованием Haskell.У Haskell есть действительно красивые и элегантные решения для каждой проблемы, которую вы бросаете, но если вы будете настаивать на том, чтобы бороться с этим, а не учиться, то вы всегда будете сталкиваться с ошибками.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...