Ral*_*lph 10 io parallel-processing haskell
下面是Haskell代码(HTTP)下载给定目录中缺少的文件:
module Main where
import Control.Monad ( filterM
, liftM
)
import Data.Maybe ( fromJust )
import Network.HTTP ( RequestMethod(GET)
, rspBody
, simpleHTTP
)
import Network.HTTP.Base ( Request(..) )
import Network.URI ( parseURI )
import System.Directory ( doesFileExist )
import System.Environment ( getArgs )
import System.IO ( hClose
, hPutStr
, hPutStrLn
, IOMode(WriteMode)
, openFile
, stderr
)
import Text.Printf ( printf )
indices :: [String]
indices =
map format1 [0..9] ++ map format2 [0..14] ++ ["40001-41284" :: String]
where
format1 index =
printf "%d-%d" ((index * 1000 + 1) :: Int)
(((index + 1) * 1000) :: Int)
format2 index =
printf "%d-%d" ((10000 + 2 * index * 1000 + 1) :: Int)
((10000 + (2 * index + 2) * 1000) :: Int)
main :: IO ()
main = do
[dir] <- getArgs
updateDownloads dir
updateDownloads :: FilePath -> IO ()
updateDownloads path = do
let
fileNames = map (\index ->
(index, path ++ "/tv_and_movie_freqlist" ++ index ++ ".html")) indices
missing <-
filterM (\(_, fileName) -> liftM not $ doesFileExist fileName) fileNames
mapM_ (\(index, fileName) -> do
let
url =
"http://en.wiktionary.org/wiki/Wiktionary:Frequency_lists/TV/2006/" ++
index
request =
Request
{ rqURI = fromJust $ parseURI url
, rqMethod = GET
, rqHeaders = []
, rqBody = ""
}
hPutStrLn stderr $ "Downloading " ++ show url
resp <- simpleHTTP request
case resp of
Left _ -> hPutStrLn stderr $ "Error connecting to " ++ show url
Right response -> do
let
html = rspBody response
file <- openFile fileName WriteMode
hPutStr file html
hClose file
return ()) missing
Run Code Online (Sandbox Code Playgroud)
我想并行运行下载.我知道par,但不确定它是否可以在IOmonad中使用,如果是的话,怎么样?
更新:这是我的代码重新实现使用Control.Concurrent.Async和mapConcurrently:
module Main where
import Control.Concurrent.Async ( mapConcurrently )
import Control.Monad ( filterM
, liftM
)
import Data.Maybe ( fromJust )
import Network.HTTP ( RequestMethod(GET)
, rspBody
, simpleHTTP
)
import Network.HTTP.Base ( Request(..) )
import Network.URI ( parseURI )
import System.Directory ( doesFileExist )
import System.Environment ( getArgs )
import System.IO ( hClose
, hPutStr
, hPutStrLn
, IOMode(WriteMode)
, openFile
, stderr
)
import Text.Printf ( printf )
indices :: [String]
indices =
map format1 [0..9] ++ map format2 [0..14] ++ ["40001-41284" :: String]
where
format1 index =
printf "%d-%d" ((index * 1000 + 1) :: Int)
(((index + 1) * 1000) :: Int)
format2 index =
printf "%d-%d" ((10000 + 2 * index * 1000 + 1) :: Int)
((10000 + (2 * index + 2) * 1000) :: Int)
main :: IO ()
main = do
[dir] <- getArgs
updateDownloads dir
updateDownloads :: FilePath -> IO ()
updateDownloads path = do
let
fileNames = map (\index ->
(index, path ++ "/tv_and_movie_freqlist" ++ index ++ ".html")) indices
missing <-
filterM (\(_, fileName) -> liftM not $ doesFileExist fileName) fileNames
pages <-
mapConcurrently (\(index, fileName) -> getUrl index fileName) missing
mapM_ (\(fileName, html) -> do
handle <- openFile fileName WriteMode
hPutStr handle html
hClose handle) pages
where
getUrl :: String -> FilePath -> IO (FilePath, String)
getUrl index fileName = do
let
url =
"http://en.wiktionary.org/wiki/Wiktionary:Frequency_lists/TV/2006/" ++
index
request =
Request
{ rqURI = fromJust $ parseURI url
, rqMethod = GET
, rqHeaders = []
, rqBody = ""
}
resp <- simpleHTTP request
case resp of
Left _ -> do
hPutStrLn stderr $ "Error connecting to " ++ show url
return ("", "")
Right response ->
return (fileName, rspBody response)
Run Code Online (Sandbox Code Playgroud)
och*_*les 13
这看起来正是它的async设计目的,实际上这个例子是并行下载.还有一个关于此的演示文稿 - http://skillsmatter.com/podcast/home/high-performance-concurrency - 非常值得一试.
Don*_*art 12
由于操作涉及IO,因此通常会/不会/使用par此操作,因为它不会对IO操作执行任何操作.
您将需要一个显式并发模型,以隐藏下载的延迟.
我推荐MVars或TVars,结合forkIO.
工作队列抽象通常对此类问题很有用:将所有URL推入队列,并为N个核心提供一组固定的工作线程(例如N*k),直到完成工作.然后将完成的工作附加到传递回主线程的通信信道.
以下是使用频道的并行URL检查器的示例.
http://code.haskell.org/~dons/code/urlcheck/Check.hs
看看mapConcurrently Simon Marlow的"异步"库.
它将一个IO动作并行和异步映射到Traversable容器的元素,并等待所有操作.
例:
{-# LANGUAGE PackageImports #-}
import System.Environment (getArgs)
import "async" Control.Concurrent.Async (mapConcurrently)
import "HTTP" Network.HTTP
import "HTTP" Network.Stream (Result)
import "HTTP" Network.HTTP.Base (Response(..))
import System.IO
import "url" Network.URL (encString)
import Control.Monad
getURL :: String -> IO (String, Result (Response String))
getURL url = do
res <- (simpleHTTP . getRequest) url
return (url, res)
main = do
args <- getArgs
case args of
[] -> putStrLn "usage: program url1 url2 ... urlN"
args -> do
results <- mapConcurrently getURL args
forM_ results $ \(url, res) -> do
case res of
Left connError -> putStrLn $ url ++ "; " ++ show connError
Right response -> do
putStrLn $ url ++ "; OK"
let content = rspBody response
-- make name from url
fname = encString True (`notElem` ":/") url ++ ".html"
writeFile fname content
Run Code Online (Sandbox Code Playgroud)