在Real World Haskell, Chapter 28, Software transactional memory中,开发了并发Web链接检查器。它获取网页中的所有链接,并使用HEAD请求点击它们中的每一个,以确定链接是否处于活动状态。采用并发方法来构建该程序,并做出以下声明:
我们不能简单地为每个URL创建一个线程,因为如果(正如我们所料)大多数链接都是实时且响应的,那么可能会使我们的CPU或网络连接负担过重。相反,我们使用固定数量的工作线程,这些线程获取要从队列中下载的URL。
我不完全理解为什么需要这个线程池而不是为每个链接使用forkIO
。 AFAIK,Haskell运行时维护一个线程池并适当地调度它们,所以我没有看到CPU过载。此外,在a discussion about concurrency on the Haskell mailing list中,我发现以下陈述朝着同一方向发展:
在Haskell中没有意义的一个范例是工作线程(因为RTS就是这样做的) 为了我们);而不是抓取一个工人,而只是forkIO。
网络部分只需要线程池,或者它也有CPU原因吗?
答案 0 :(得分:23)
我认为,核心问题是网络方面。如果每个链接有10,000个链接和forkIO,那么你可能有10,000个套接字,你试图一次打开,这取决于你的操作系统的配置方式,甚至可能都不可能,效率低得多。
然而,事实上我们有绿色线程在多个操作系统线程(理想情况下是坚持单个内核)上进行“虚拟”调度并不意味着我们可以随意分配工作而不考虑cpu使用情况。这里的问题不在于我们不会为我们处理CPU本身的调度,而是上下文切换(甚至是绿色的)成本周期。每个线程,如果它处理不同的数据,将需要将该数据拉入cpu。如果有足够的数据,这意味着将内容拉入和退出cpu缓存。即使不存在,也意味着将事物从缓存中提取到寄存器等等。
即使一个问题是平凡的并行,但实际上永远不会正确的想法,只是尽可能地将其分解并尝试“一下子”。