具有大量文件IO任务的多线程

时间:2015-09-26 20:22:23

标签: c# multithreading

我不是C#的新手,但我对语言不熟悉,知道如何做我需要做的事。

我有一个文件,称之为File1.txt。 File1.txt有100,000行左右。 我将复制File1.txt并将其命名为File1_untested.txt。 我还将创建一个空文件“Successes.txt” 对于文件中的每一行:

  • 从File1_untested.txt
  • 中删除此行
  • 如果此行通过测试,请将其写入Successes.txt

所以,我的问题是,我怎样才能多线程?

到目前为止,我的方法是创建一个对象(LineChecker),给对象提供要检查的行,并将对象传递给ThreadPool。我了解如何使用ThreadPools将一些任务与CountdownEvent一起使用。但是,一次排队100,000个任务似乎是不合理的。我该怎样逐渐喂养游泳池?也许一次1000行或类似的东西。

另外,我需要确保没有两个线程同时添加到Successes.txt或从File1_untested.txt中删除。我可以用lock()处理这个,对吗?我应该将什么传递给lock()?我可以使用LineChecker的静态成员吗​​?

我只是想广泛了解如何设计这样的东西。

2 个答案:

答案 0 :(得分:2)

由于测试需要相对较长的时间,因此使用多个CPU内核是有意义的。但是,这种利用只能用于相对昂贵的测试,而不能用于读取/更新文件。这是因为读取/更新文件相对便宜。

以下是您可以使用的一些示例代码:

假设你有一个相对昂贵的测试方法:

$('input').keyup(function() {
  $(this).val($(this).val().trim());
});

这是一个可以利用多个CPU进行测试的代码示例:

这里我们将集合中的项目数限制为10,这样从文件中读取的线程将等待其他线程赶上,然后再从文件中读取更多行。

这个输入线程的读取速度比其他线程可以测试的速度快得多,所以在最坏的情况下,我们将读取比测试线程测试更多的10行。这可以确保我们有良好的内存消耗。

private bool Test(string line)
{
    //This test is expensive
}

答案 1 :(得分:2)

如果你的“测试”速度很快,那么多线程就不会给你带来任何好处,因为你的代码将是100%磁盘绑定的,并且可能你的所有文件都放在同一个磁盘上:你无法改进具有多线程的单个磁盘的吞吐量。

但是,由于您的“测试”将等待来自网络服务器的响应,这意味着测试将变得缓慢,因此多线程有很大的改进空间。基本上,您需要的线程数取决于Web服务器可以同时处理多少请求而不会降低Web服务器的性能。这个数字可能仍然很低,所以你可能最终没有获得任何东西,但至少你可以试试。

如果您的文件不是很大,那么您可以立即阅读所有文件,并立即全部写完。如果每行只有80个字符,那么这意味着你的文件只有8兆字节,这就是花生,所以你可以将所有行读入列表,在列表上工作,生成另一个列表,最后写出来整个清单。

这将允许您创建一个结构,比如MyLine,其中包含每行的索引和每行的文本,以便您可以在编写之前对所有行进行排序,这样您就没有了担心来自服务器的无序响应。

然后,您需要做的是使用像@Paul建议的BlockingCollection这样的边界阻塞队列。

BlockingCollection接受构造函数参数作为其最大容量。这意味着一旦达到其最大容量,将阻止添加到其中的任何进一步尝试(调用者在那里等待),直到删除某些项目。因此,如果您希望最多有10个同时挂起的请求,您可以按如下方式构建它:

var sourceCollection = new BlockingCollection<MyLine>(10);

你的主线程将填充sourceCollectionMyLine个对象,你将有10个线程阻止等待从集合中读取MyLine。每个线程向服务器发送请求,等待响应,将结果保存到线程安全resultCollection,并尝试从sourceCollection获取下一个项目。

您可以使用C#的async功能,而不是使用多个线程,但我对它们并不十分熟悉,因此我无法就您将如何做到这一点向您提供建议。

最后,将resultCollection的内容复制到List,对列表进行排序,然后将其写入输出文件。 (将副本复制到单独的List可能是一个好主意,因为对线程安全resultCollection进行排序可能比排序非线程安全{{1}慢得多我说可能是。)