为什么我的并行遍历Haskell程序泄漏内存?

时间:2015-09-07 19:58:40

标签: haskell memory-leaks space-leak

考虑以下Haskell程序(我这样做主要是出于学习目的):

import qualified Control.Concurrent.MSem as Sem
import System.Environment (getArgs)
import Control.Concurrent (forkIO)
import Control.Monad

-- Traverse with maximum n threads
parallelTraverse :: Foldable a => Int -> (b -> IO()) -> a b -> IO ()
parallelTraverse n action values = do
  sem <- Sem.new n
  forM_ values $ \value -> Sem.with sem (forkIO $ action value)

main :: IO ()
main = do
  args <- getArgs
  let nThreads = read . head $ args :: Int
  parallelTraverse nThreads print [(1::Int)..]

当我运行它时,内存很快就会升至几GB。我尝试了各种组合以确保丢弃中间计算的结果(打印操作)。为什么它还在泄漏空间?

1 个答案:

答案 0 :(得分:6)

首先,你有一个明显的错误:

forkIO (Sem.with sem (action value))

您正在围绕“fork”操作而不是其中的操作从主线程寻址信号量。以下是实施它的正确方法:

parallelTraverse

即,从分叉线程的上下文中解决信号量。

其次,在以下代码中,您将在无限列表中调用parallelTraverse nThreads print [(1::Int)..] 操作:

forkIO

这会导致线程无限分叉。由于with操作对于调用线程来说大致是即时的,因此很快就会耗尽资源。

要使用信号量来限制工作线程数,wait模式在您的情况下根本不会。相反,您应该使用signalparallelTraverse :: Foldable a => Int -> (b -> IO()) -> a b -> IO () parallelTraverse n action values = do sem <- Sem.new n forM_ values $ \value -> do Sem.wait sem forkIO $ finally (action value) (Sem.signal sem) 的显式组合,并且不要忘记正确处理异常(如果您期望它们)。 。E.g,:

// Jquery wrapper for drupal to avoid conflicts between libraries.
(function ($) {
  // Jquery onload function.

Drupal.behaviors.ajax_privacy = {
attach: function (context, settings) {
// Your JS code.

$.get('ajax/call',null,ajaxAction);
return false;
}
};

ajaxAction=function(response)
  {
  alert(response);
  };
})(jQuery);