如何同时从两个程序写入单个 xml 文件?
经过多年使用旧 INI 文件的经验后,我们最近开始使用 XML 文件。
我的同事发现了一些使用 System.Xml.XmlDocument.Save 的 CodeProject 示例代码。当两个程序尝试同时写入同一个文件时,我们会遇到异常。
System.IO.IOException:进程 无法访问文件“C:\Test.xml” 因为它正在被另一个人使用 流程。
事后看来,这似乎是显而易见的,但我们没有预料到这一点,因为通过 Win32 API 访问 INI 文件没有此限制。我假设 Win32 调用完成了一些仲裁,这些仲裁在比 XmlDocument.Save 方法更高的级别上工作。
我希望 .Net 库中有更高级别的 XML 例程,其工作方式与 Win32 函数类似,但不知道从哪里开始寻找。
或者我们可以设置文件访问权限以允许多个程序写入同一个文件?
时间很短(就像几乎所有的软件项目一样),如果我们不能快速找到解决方案,我们就必须捂住鼻子,回到 INI 文件。
We recently started working with XML files, after many years of experience with the old INI files.
My coworker found some CodeProject sample code that uses System.Xml.XmlDocument.Save. We are getting exceptions when two programs try to write to the same file at the same time.
System.IO.IOException: The process
cannot access the file 'C:\Test.xml'
because it is being used by another
process.
This seems obvious in hindsight, but we had not anticipated it because accessing INI files via the Win32 API does not have this limitation. I assume there's some arbitration done by the Win32 calls that work at a higher level than the the XmlDocument.Save method.
I'm hoping there are higher level XML routines somewhere in the .Net library that work similarily to the Win32 functions, but don't know where to start looking.
Or maybe we can set up our file access permissions to allow multiple programs to write to the same file?
Time is short (like almost all SW projects), and if we can't find a solution quickly, we'll have to hold our noses and go back to INI files.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
这是行不通的。即使这两个进程可以写入同一个文件,它也会损坏您的文件。如果你确实需要同时编写多个程序,我会使用数据库。
或者您可以将单个 XML 文件拆分为单独的文件吗?让每个进程写入自己的文件,然后在“读取”方法中将所有文件组合在一起。
This isn't going to work. Even if the 2 processes could write to the same file, it would just corrupt your file. If you really need multiple programs writing at the same time, I would use a database instead.
Or could you split your single XML file up into separate files? Let each process write to its own file, then combine all of the files together during your "Read" method.
如果这些程序在同一个会话中运行,您可以创建一个单独的类来处理文件写入,并使用 Singleton(或多例映射到多个文件名)设计模式来保证该类只有一个实例。这将消除 I/O 异常。只需确保在每个“写入文件”请求时刷新这个新类中的缓冲区即可。
请注意:这将为您的程序引入全局状态。由于这个原因,Singleton 被一些人认为是一种反模式。
此外,这对于 XML 文件可能不太适用,因为它们形成树结构,这意味着在对其进行写入更改之前必须重新读取整个文件(不能简单地“附加”到末尾)文件的内容,否则您将损坏它)。
我认为您需要检查一下尝试使用 XML 的原因。如果您尝试像“哑数据库”一样使用它,则应该考虑迁移到真正的“精简版”数据库,例如 SQLite。如果您只需要重复追加到文件末尾,那么使用 .ini 这样的平面文件没有任何问题。
If these programs are running in the same session, you can create a separate class to handle writing to files, and use either Singleton (or Multiton to map to many file names) design pattern to guarantee only one instance of this class. This will eliminate your I/O exceptions. Just make sure that you flush the buffer in this new class on each "write to file" request.
Beware: this will introduce a global state to your program. Singleton is considered by some to be an anti-pattern for this reason.
In addition, this may not work well for XML files because they form a tree structure, which means that the whole file has to be re-read before you make a write change to it (you can't simply "append" to the end of the file or you will corrupt it).
I think you need to examine the reason why you are trying to use XML. If you are trying to use it like a "dumb database", you should consider moving to a real "lite" database like SQLite. If you just need to repeatedly append to the end of the file, there is nothing wrong with using a flat file like an .ini.
好吧,您可以通过确保在写入文件时关闭文件并重试打开它来解决第一个问题。然后,您需要将其保持打开状态,读取它,合并所有更改,然后再次将其写出。 (您必须合并更改,否则您将覆盖应用程序读取文件和再次写出文件之间所做的更改)。
INI 文件已被取代两次 - 一次被注册表取代,然后被 ConfigurationManager 您可能想尝试一下 ConfigurationManager。如果文件发生更改,Configuration.Save 似乎会引发异常或者它无法写入 - 在这种情况下捕获异常并重试几次。
Well, you can solve your first problem by making sure you close the file when you write it, and retry to open it. You then need to hold it open, read it, merge any changes, then write it out again. (you have to merge the changes, otherwise you will overwrite changes made between your application reading the file and writing it out again).
INI files have been superseded twice - once by the registry, and then by ConfigurationManager You might want to experiment with ConfigurationManager. It appears Configuration.Save will throw an exception if the file has changed or it can't write to it - in which case catch the exception and retry a few times.
分割文件的另一种方法是使用数据库 - 有 XML 数据库,Wikipedia 列出了其中一些。
但我不能为他们中的任何一个人提供保证。
An alternative to splitting the file would be to use a database - there are XML Databases out there, Wikipedia has some of them listed.
I can't vouch for any of them though.
据我所知,很难让多个进程写入到一个文件 - 如何处理并发?
但是,如果您希望一个文件读取,而另一个文件写入,则可以(只要您不介意读者可能读取稍微旧的文件)。如果是这样,请使用传入以只读文件访问权限初始化的 FileStream 的
XmlDocument.Load
函数。许多(大多数?) System.Xml 命名空间也将具有线程安全重载 - 但听起来您想要跨进程文件访问,因此除非您可以将应用程序的所有实例折叠到单个进程中,否则这将无济于事。
因此,如果我是你,我会考虑使用更丰富的支持多进程访问的数据存储。某种数据库将是最明显的答案。
编辑:根据评论更正。
As far as I'm aware, it's difficult to get multiple processes write to a file - how to handle concurrency?
However, if you want one file to read, while the other writes, that will work (as long as you don't mind the reader possibly reading a slightly old file). If so, use the
XmlDocument.Load
functions passing in a FileStream that was initialized with read only file access.Many (most?) of the System.Xml namespace will have thread safe overloads too - but it sounds like you want cross process file access so that will not help unless you can collapse all of your instances of your applications into a single process.
So therefore, if I were you I would consider a richer data store that supports multi-process access. A database of some kind would be the most obvious answer.
EDIT: Corrected as per comment.
XML 文件对其结构(架构/DTD 等)比 INI 文件更敏感。此外,Win32 中的 INI 方法向您隐藏了文件访问的复杂性。理论上,您可以想出某种在标签级别而不是文件级别锁定的线程安全编写器,但这不是开箱即用的功能。
An XML file is much more sensitive to its structure (schema/DTD, etc.) than an INI file. Also, the INI methods in Win32 hide the complexities of file access from you. In theory you could come up with some kind of thread-safe writer that locked at a tag level rather than file level, but it's not an out-of-the-box feature.