多个线程从同一个文件中读取数据

20

我有一个需要被多次读取的xml文件,希望使用Parallel.ForEach来加速处理,因为所读取的数据与读取顺序无关,只是用于填充对象。我的问题是,即使在每个线程中以只读方式打开文件,它仍然抱怨文件已被其他程序打开。(我没有在文本编辑器或其他地方打开它:))

我该如何实现从同一文件进行多次读取?

编辑:文件大小约为18KB,非常小。需要从中读取大约1800次。

谢谢


4
您的硬盘仍然只有一个磁头,因此一次只能进行一次读取。您期望获得什么样的性能提升? - Daniel
1
文件是否适合磁盘缓存?如果是,那么多次读取将会很快。否则,如果文件足够小可以放入内存中,则应使用内存映射文件,并从中读取。否则,硬盘将花费大量时间进行交换而不是执行顺序读取,这将导致性能下降而不是提高。 - mdma
1
硬盘上只有一个磁头吗?我很确定即使是单碟片驱动器现在也有多个磁头以及缓存等技术来解决磁头特定的减速问题。 - GrayWizardx
1
@GrayWizardx:错过重点的好方法 :) - Timwi
@Timwi,我猜我做到了。哪个点?我提供了一个替代方案来解决多次读取的问题。严格指定FileShare.Read可以修复错误,但不能解决问题。 - GrayWizardx
@GrayWizardx:我只是在针对上面的评论发表看法,并没有针对你的回答。 - Timwi
4个回答

41

如果你想让多个线程从同一个文件中读取,你需要指定 FileShare.Read

using (var stream = File.Open("theFile.xml", FileMode.Open, FileAccess.Read, FileShare.Read))
{
    ...
}

然而,你不会因此获得任何加速,原因如下:

  1. 你的硬盘一次只能读取一件事情。虽然你有多个线程同时运行,但这些线程最终都将互相等待。
  2. 你不能轻易地解析XML文件的一部分。通常情况下,你需要每次解析整个XML文件。由于你有多个线程一直在读取它,似乎你并不希望文件发生变化。如果是这样的话,那么你为什么需要多次读取它呢?

1
对于“1”和“2”评论点赞。当多个线程尝试读取同一文件时,FileAccess.Read和FileShare.Read都无法解决问题。因此,如“2”评论中所述,如果我们只需要读取它,则可以先读取一次,然后将其提供给“处理”多个线程。 - vts123
关于第一点,这并不适用于诸如磁盘阵列之类的东西,对于服务器软件来说,这是很容易预期的。 - Matt

6
根据文件大小和读取类型的不同,将文件先加载到内存中,然后直接为您的线程提供访问可能更快。由于您没有提供文件、读取等具体信息,因此我无法确定它是否能满足您的具体需求。一般的做法是在单个线程中加载文件,然后通过 Xml 结构直接或间接(通过 XmlNodes 等)向每个线程提供文件访问权限。我设想类似于以下步骤:1. 加载文件;2. 对于每个 Xpath 查询,将匹配的节点分派给您的线程。如果线程不直接修改 XML,则这可能是一个可行的替代方案。

3
打开文件时,需要指定FileShare.Read
using (var stream = new FileStream("theFile.xml", FileMode.Open, FileAccess.Read, FileShare.Read))
{
    ...
}

那样文件就可以被多次打开以供阅读。

4
尽管这是正确的,但如果张贴者未将文件分成块,则使用多个线程几乎不可能实现真正的加速。 - Mitch Wheat
3
@Mitch Wheat: 确实。但我只是回答原帖的问题,而不是判断使用多个线程是否是一个好主意 ;) (翻译:确实。但我只是回答原帖的问题,不评判使用多个线程是否明智;)) - Thomas Levesque

-1

虽然这是一篇旧文章,但似乎很受欢迎,所以我想添加一个解决方案,我已经成功地用于需要对文件进行读取访问的多线程环境。但是,文件必须足够小,以至于在处理期间至少可以保存在内存中,并且在共享访问期间只能读取文件而不能写入。

string FileName = "TextFile.txt";
string[] FileContents = File.ReadAllLines(FileName);

foreach (string strOneLine in FileContents)
{
  // Do work on each line of the file here
}

只要文件仅被读取,多个线程或程序可以同时访问和处理它,而不会互相干扰。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接