Haskell - 关于System.Process和多线程的一些问题

时间:2010-01-19 14:16:48

标签: multithreading haskell operating-system

我在C中有一个小的数值模拟(我必须在C中与我的顾问分享它)但我想使用“haskell脚本”之类的东西来组织模拟。该程序接受一些命令行参数并吐出一些我希望重定向到文件的输出,所以我做了类似这样的事情:

 import Control.Monad
 import System.Process

我有一个函数来创建输出文件的名称:

filename :: Int -> String  
filename n = some stuff here...

和我想要的命令:

command :: Int -> String
command n = "./mycutesimulation " ++ show n ++ " >" ++ filename n

最后我制作了一个我想要制作的曲目列表并使用runCommand运行它们:

commands = map command [1,2..1000]

main = do
   sequence_ $ map runCommand commands

问题是我运行这个“脚本”后,我的计算机几乎冻结了负载。正在执行的程序在内存使用方面非常轻,并且在几分之一秒内运行。这不应该发生。

所以,我的问题是:

1)我是否只是同时执行了1000个进程?如何以合理的顺序执行它们 - 顺序执行或一次只执行几个进程。

2)我在四核中运行它,这对我有利。有没有办法可以使用-threaded标志编译它,并以有组织的方式同时执行进程?

3 个答案:

答案 0 :(得分:4)

您需要waitForProcess =<< runCommand

import System.Process

main = sequence $ map (\x -> runCommand x) commands
 where commands = map (\x -> "echo " ++ show x) [1, 2..1000]

与你的症状相似,但

import System.Process

main = sequence $ map (\x -> waitForProcess =<< runCommand x) commands
 where commands = map (\x -> "echo " ++ show x) [1, 2..1000]

作品。

答案 1 :(得分:3)

首先,你应该检查top或任务管理器,看看你是否确实快速连续创建了1000个进程,然后根据它找到解决方案。

减慢流程创建的一种简单方法是在创建下一个流程之前等待每个流程完成。因此,不应在您的runCommand上映射commands,而应映射您自己的函数,该函数首先调用runCommand,然后在返回的waitForProcess上调用ProcessHandle,即每次调用您的辅助函数将被阻塞,直到生成的进程完成。

上述解决方案的缺点是它只会使用您的四个核心之一。那么你可以做的就是使用所有四个核心来partition commands到四个(或你想要使用多少个核心)列表,然后用forkIO生成四个工作线程对于每个子列表,每个子列表都在该子列表上运行map

顺便说一下。 mapM_ f == sequence_ . map f

答案 2 :(得分:1)

这是一个快速而肮脏的“一次运行几个”,如果它有帮助:

import System.Process

commands = replicate 16 "sleep 2"

runSome handles cmd = do
    (h:hs) <- handles
    waitForProcess h
    h' <- runCommand cmd
    return $ hs ++ [h']

test n = 
    let initial = mapM runCommand $ take n commands
    in foldl runSome initial (drop n commands)

这只是(mis)使用一个列表作为一个简单的队列,运行尽可能多的命令,然后等待队列前面的命令,并在完成后添加一个新命令。请注意,如果混合了一些长时间运行的命令,这将不会理想,但可能就足够了。但是,请不要认为这是一种“正确”的方式。