34

Real World Haskell,第 28 章,软件事务内存中,开发了一个并发 Web 链接检查器。它获取网页中的所有链接,并使用 HEAD 请求命中每一个链接,以确定链接是否处于活动状态。采用并发的方法来构建这个程序,并做出以下声明:

我们不能简单地为每个 URL 创建一个线程,因为如果(如我们所料)大多数链接都是活动的和响应式的,这可能会使我们的 CPU 或网络连接负担过重。相反,我们使用固定数量的工作线程,它们从队列中获取要下载的 URL。

我不完全理解为什么需要这个线程池而不是forkIO为每个链接使用。AFAIK,Haskell 运行时维护一个线程池并适当地安排它们,所以我没有看到 CPU 过载。此外,在Haskell 邮件列表上关于并发的讨论中,我发现以下语句朝着相同的方向发展:

在 Haskell 中没有意义的一种范式是工作线程(因为 RTS 为我们做到了);而不是取一个工人,只是 forkIO 代替。

线程池是否只需要网络部分,还是有 CPU 原因?

4

1 回答 1

23

我想,核心问题是网络方面。如果您有 10,000 个链接和每个链接的 forkIO,那么您可能有 10,000 个套接字尝试一次打开,这取决于您的操作系统的配置方式,甚至可能都不可能,效率要低得多。

然而,我们拥有跨多个操作系统线程“虚拟”调度的绿色线程这一事实并不意味着我们可以随机分配工作而不考虑 cpu 使用情况。这里的问题不在于 CPU 本身的调度不会为我们处理,而是上下文切换(甚至是绿色切换)会花费周期。如果每个线程处理不同的数据,则需要将该数据拉入 cpu。如果有足够的数据,这意味着将东西拉入和拉出 cpu 缓存。即使没有,这也意味着将缓存中的内容拉到寄存器等。

即使一个问题是微不足道的平行问题,将它分解得尽可能小并尝试“一次完成”实际上也不是正确的想法。

于 2013-03-04T04:47:53.163 回答