我最近读了很多关于如何编写多线程应用程序是一个巨大的痛苦,并已经足够了解该主题,至少在某种程度上,为什么会这样理解.
我已经读过使用函数式编程技术可以帮助缓解一些痛苦,但我从未见过一个简单的并发功能代码示例.那么,使用线程有哪些替代方案呢?至少,有哪些方法可以将它们抽象出来,这样你就不必考虑锁定之类的事情以及特定库的对象是否是线程安全的.
我知道谷歌的MapReduce应该可以解决问题,但我还没有看到它的简洁解释.
虽然我在下面给出一个具体的例子,但我对一般技术比对解决这个特定问题更感兴趣(使用这个例子来帮助说明其他技术会有所帮助).
当我写一个简单的网络爬虫作为学习练习时,我提出了这个问题.它工作得很好,但速度很慢.大部分瓶颈来自下载页面.它目前是单线程的,因此一次只下载一个页面.因此,如果可以同时下载页面,即使爬虫在单个处理器计算机上运行,也会大大加快速度.我考虑使用线程来解决问题,但他们吓到了我.关于如何在不释放可怕的线程噩梦的情况下为这类问题添加并发性的任何建议?
解决方法
我将添加一个示例,说明如何使用功能代码安全地使代码并发.
以下是您可能希望并行执行的一些代码,因此您无需等待一个文件完成以开始下载下一个文件:
void DownloadHTMLFiles(List<string> urls) { foreach(string url in urls) { DownlaodOneFile(url); //download html and save it to a file with a name based on the url - perhaps used for caching. } }
如果您有多个文件,用户可能会花一分钟或更长时间等待所有文件.我们可以像这样在功能上重写这段代码,它基本上完全相同:
urls.ForEach(DownloadOneFile);
请注意,这仍然按顺序运行.然而,它不仅更短,我们在这里获得了重要的优势.由于每次对DownloadOneFile函数的调用都与其他函数完全隔离(出于我们的目的,可用带宽不是问题),您可以非常轻松地将ForEach函数替换为另一个非常类似的函数:一个启动对DownlaodOneFile的每次调用的函数来自线程池的单独线程.
事实证明.Net使用Parallel Extensions就可以实现这样的功能.因此,通过使用函数式编程,您可以更改一行代码,并突然有一些并行运行的东西,用于顺序运行.这非常强大.