我有一个表示shell命令的字符串列表,我想同时执行10个,例如10个。这是我第一次真正涉足Haskell的并发编程,我遇到了一些麻烦。
我的想法是将列表分成十个元素的子列表,然后mapM_
一个函数同时生成十个命令中的每一个,等待每个命令完成,然后再转到下一组十。然而,我使用/采用的每个不同的库/方法似乎只是同时启动列表中的每个命令。
假设我有一个最简单的函数来执行shell命令:
import System.Process
execShellCommand :: String -> IO ()
execShellCommand cmd = void $ createProcess (shell cmd)
天真的方法
import Control.Monad
import Data.List.Split.Internals
runChunks :: [String] -> IO ()
runChunks as = mapM_ (mapM_ execShellCommand) $ chunksOf 10 as
一次执行列表中的所有命令(我尝试使用Conduit.Process中的waitFor
函数,结果相似)。让我们尝试更加周到,并重新定义函数execShellCommand
以使用Control.Concurrent
:
import Control.Concurrent
execShellCommand :: String -> IO ()
execShellCommand cmd = do
m <- newEmptyMVar
r <- void $ createProcess (shell cmd)
forkIO $ putMVar m r
takeMVar m
runChunks :: [String] -> IO ()
runChunks [] = return ()
runChunks as = do
mapM_ execShellCommand $ take 10 as
runChunks $ drop 10 as
也做同样的事情(一次运行列表中的所有命令)。另一个具有相同结果的图书馆:
import Control.Concurrent.Async
runChunks :: [String] -> IO ()
runChunks [] = return ()
runChunks as = do
ck <- mapM (async . execShellCommand) $ take 10 as
mapM_ wait ck
runChunks $ drop 10 as
我要么不在这里抓住一个概念,在这种情况下,我非常欣赏澄清的解释,或者我只是没有看到能够实现我瞄准的效果的库函数对于;对于后者,一个例子将是非常有帮助的。非常感谢你。
答案 0 :(得分:2)
我对你使用waitFor
尝试的内容感到有点好奇。这是我的第一次尝试:
import System.Process
commands = ["./demo.sh " ++ show n | n <- [1..10]]
chunksOf _ [] = []
chunksOf n xs = take n xs : chunksOf n (drop n xs)
spawn cmd = do
(_,_,_,proc) <- createProcess (shell cmd)
return proc
spawnAndAwaitGroup cmds = do
procs <- mapM spawn cmds
mapM_ waitForProcess procs
main = do
mapM_ spawnAndAwaitGroup (chunksOf 3 commands)
注意IO动作是如何组成的;等待需要适应生成,所以我们在块级别应用这两个操作。我们可以集体创建spawn
个动作,但waitForProcess
需要返回的句柄。因此,我使用了mapM
和do
<-
。
在demo.sh
:
#!/bin/sh
echo $1 starting
sleep 3
echo $1 ending
输出:
2 starting
1 starting
3 starting
3 ending
1 ending
2 ending
4 starting
5 starting
6 starting
4 ending
5 ending
6 ending
7 starting
8 starting
9 starting
7 ending
8 ending
9 ending
10 starting
10 ending
值得注意的是,这是一个纯粹的顺序程序,而不是Haskell级别的并发程序。它等同于这个shell脚本:
./demo.sh 1 &
./demo.sh 2 &
./demo.sh 3 &
wait
./demo.sh 4 &
./demo.sh 5 &
./demo.sh 6 &
wait
./demo.sh 7 &
./demo.sh 8 &
./demo.sh 9 &
wait
./demo.sh 10 &
wait
所有并发行为都发生在操作系统级别,在生成的进程的交互中。