我正在读Haskell中的并行并行编程的第11章。在这本书中,以下程序用于并发获取URL:
main = do
xs <- foldr conc (return []) (map getURL sites)
print (map B.length xs)
where
conc ioa ioas = do
(a,as) <- concurrently ioa ioas
return (a:as)
假设"get“作业是[g0, g1, g2]
。据我所知,xs
将绑定到以下结构:
g0 `conc` (g1 `conc` (g2 `conc` (return [])))
现在,我的问题是在本例中将创建多少个线程。我的猜测是,将创建7个线程(与我期望用于此任务的4个线程相反):
thread0: g0 `conc` (g1 `conc` (g2 `conc` (return [])))
thread1: g0
thread2: (g1 `conc` (g2 `conc` (return [])))
thread3: g1
thread4: (g2 `conc` (return []))
thread5: g2
thread6: return []
这个推理正确吗?
发布于 2016-08-07 20:00:32
所有的猜测都是错误的!实际上,有六个线程生成了。
把大锤拿出来-
import Control.Concurrent.Async
import Control.Concurrent (myThreadId)
import qualified Data.ByteString as B
-----------------------------------------------------------------------------
sites = ["http://www.google.com",
"http://www.wikipedia.com/wiki/Spade",
"http://www.wikipedia.com/wiki/Shovel"]
getURL _ = return B.empty
annotate :: IO a -> IO a
annotate action = do
tid <- myThreadId
putStrLn $ "I'm a thread and my ID is: " ++ show tid
action
-- <<main
main = do
xs <- foldr conc (return []) (map getURL sites)
print (map B.length xs)
where
conc ioa ioas = do
(a,as) <- concurrently (annotate ioa) (annotate ioas)
return (a:as)
-- >>
传递给concurrently
的所有操作都将使用annotate
包装,这将导致线程将其ThreadId
阻塞为stdout。我得到了-
+$ ./geturls9 9我是一个线程,我的ID是: ThreadId 5,我是一个线程,我的ID是: ThreadId 6,我是一个线程,我的ID是: ThreadId 8,我是一个线程,我的ID是: ThreadId 9,我是一个线程,我的ID是: ThreadId 10,我是一个线程,我的ID是: ThreadId 7,0,0,0,0
当然,由于GHC版本和赛车的不同,您对同一程序的结果几乎肯定会在ID号和订单的细节上有所不同。
所以,你的直觉很好!您的分析只需要一个,因为整个表达式g0 `conc` (g1 `conc` (g2 `conc` (return [])))
本身并没有传递给concurrently
,所以您的thread0
当然只是主线程,而不是由异步库创建的线程。
请注意,异步为并发处理任何mapConcurrently
提供了Traversable
,因此不必使用foldr
构建自己的foldr
。为什么在Haskell的并行和并发编程中没有提到这个函数,我不确定。它是在异步-2.0.1.0中引入的,Haskell平台版本2012.4.0.0 ( PCPH介绍中提到了所有测试代码)都包含异步-2.0.1.3。这可能是教学上的原因,也许是发布中常见的延迟,再加上代码是针对旧版本的平台/库开发的,谁知道呢。该介绍还提到,“随着平台新版本的发布,样例代码将被更新。”如果你有强烈的感觉,提交一个雷特姆!
https://stackoverflow.com/questions/38812494
复制相似问题