0

经过多年处理旧 INI 文件的经验,我们最近开始使用 XML 文件。

我的同事发现了一些使用 System.Xml.XmlDocument.Save 的 CodeProject 示例代码。当两个程序尝试同时写入同一个文件时,我们会遇到异常。

System.IO.IOException:该进程无法访问文件“C:\Test.xml”,因为它正被另一个进程使用。

事后看来,这似乎很明显,但我们没有预料到,因为通过 Win32 API 访问 INI 文件没有这个限制。我假设 Win32 调用在比 XmlDocument.Save 方法更高的级别上完成了一些仲裁。

我希望.Net 库中的某个地方有更高级别的 XML 例程,它们的工作方式类似于 Win32 函数,但不知道从哪里开始寻找。

或者也许我们可以设置我们的文件访问权限以允许多个程序写入同一个文件?

时间很短(就像几乎所有的 SW 项目一样),如果我们不能快速找到解决方案,我们将不得不捂住鼻子并返回 INI 文件。

4

6 回答 6

5

这是行不通的。即使 2 个进程可以写入同一个文件,它也只会损坏您的文件。如果你真的需要同时编写多个程序,我会改用数据库。

或者您可以将单个 XML 文件拆分为单独的文件吗?让每个进程写入自己的文件,然后在“读取”方法期间将所有文件组合在一起。

于 2010-06-10T16:49:38.327 回答
2

如果这些程序在同一个会话中运行,您可以创建一个单独的类来处理对文件的写入,并使用 Singleton(或 Multiton 以映射到许多文件名)设计模式来保证此类只有一个实例。这将消除您的 I/O 异常。只需确保在每个“写入文件”请求时刷新这个新类中的缓冲区即可。

注意:这将为您的程序引入全局状态。由于这个原因,Singleton 被一些人认为是一种反模式。

此外,这可能不适用于 XML 文件,因为它们形成树结构,这意味着在对它进行写入更改之前必须重新读取整个文件(不能简单地“追加”到末尾)文件,否则您将损坏它)。

我认为您需要检查您尝试使用 XML 的原因。如果您尝试将其用作“哑数据库”,则应考虑迁移到真正的“精简”数据库,例如 SQLite。如果您只需要重复附加到文件的末尾,那么使用像 .ini 这样的平面文件并没有错。

于 2010-06-10T17:12:36.130 回答
1

据我所知,很难让多个进程写入一个文件 - 如何处理并发?

但是,如果您希望一个文件读取,而另一个文件写入,那将起作用(只要您不介意阅读器可能读取稍微旧的文件)。如果是这样,请使用XmlDocument.Load传入以只读文件访问权限初始化的 FileStream 的函数。

许多(大多数?) System.Xml 命名空间也将具有线程安全重载 - 但听起来您想要跨进程文件访问,因此除非您可以将应用程序的所有实例折叠到单个进程中,否则这将无济于事。

因此,如果我是你,我会考虑支持多进程访问的更丰富的数据存储。某种数据库将是最明显的答案。

编辑:根据评论更正。

于 2010-06-10T16:56:05.020 回答
1

拆分文件的另一种方法是使用数据库 - 那里有 XML 数据库,维基百科列出了其中的一些。

我不能保证他们中的任何一个。

于 2010-06-10T16:58:55.280 回答
1

好吧,您可以通过确保在编写文件时关闭文件并重试打开它来解决您的第一个问题。然后你需要打开它,阅读它,合并任何更改,然后再写出来。(您必须合并更改,否则您将覆盖在应用程序读取文件和再次写入文件之间所做的更改)。

INI 文件已被两次取代 - 一次被注册表取代,然后被ConfigurationManager取代您可能想尝试使用 ConfigurationManager。如果文件已更改或无法写入,则Configuration.Save似乎会引发异常 - 在这种情况下,捕获异常并重试几次。

于 2010-06-10T16:58:59.460 回答
0

与 INI 文件相比,XML 文件对其结构(模式/DTD 等)更为敏感。此外,Win32 中的 INI 方法对您隐藏了文件访问的复杂性。从理论上讲,您可以想出某种线程安全的编写器,它锁定在标签级别而不是文件级别,但这不是一个开箱即用的功能。

于 2010-06-10T17:01:52.603 回答