【问题标题】:A list of actions effected concurrently in parts部分同时执行的操作列表
【发布时间】:2018-06-14 03:32:16
【问题描述】:

我有一个代表 shell 命令的字符串列表,我想同时执行 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

我要么在这里没有掌握一个概念,在这种情况下,我非常感谢一个澄清的解释,或者我只是没有看到一个库函数可以达到我想要的效果;对于后者,一个例子将非常有帮助。非常感谢您。

【问题讨论】:

  • 无关:您不应该导入Internal 模块。在这种情况下,您可以改为导入Data.List.Split
  • 查看此答案stackoverflow.com/a/29155440/1364288 了解另一种不涉及分块的方法。

标签: haskell asynchronous concurrency


【解决方案1】:

我有点好奇你用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 需要返回的句柄。因此我使用了mapMdo &lt;-

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

所有并发行为都发生在操作系统级别,在衍生进程的交互中。

【讨论】:

    猜你喜欢
    • 2018-05-10
    • 1970-01-01
    • 2019-09-13
    • 2013-08-27
    • 2023-03-17
    • 1970-01-01
    • 1970-01-01
    • 2014-07-15
    • 1970-01-01
    相关资源
    最近更新 更多