我正在尝试通过同时在多台计算机上运行的多个进程在XML文件中实现信息更新。我的想法是循环10分钟,尝试打开并锁定文件以随机间隔(最多1秒)进行写入。打开并锁定文件后,我将加载所有XML,添加当前计算机的信息,对XML进行排序,然后转售并删除锁定,以便可以打开下一台计算机。问题在于Get-Content不会锁定文件,因此两台计算机可以加载相同的XML,而不是第二台计算机使用第一台计算机的数据加载XML。 我发现this,它提供了一种锁定文件,然后按流读取的方式,但是当我尝试对此进行修改时
$file = [IO.File]::Open($path, 'Open', 'ReadWrite', 'None')
$xml = Get-Content $path
我得到一个错误,因为文件被锁定。似乎Get-Content不会锁定文件,但是它确实尊重已经存在的锁定。 那么,有没有一种方法可以锁定文件,以便只有计算机锁定才能读写? 也许更重要的是,这是否是正确的方法,还是存在其他一些方法来进行多个XML访问?似乎这是一种常见的情况,因此即使没有本机cmdlet方法,也必须有一些最佳实践方法。 FWIW,我必须重新支持PowerShell 2.0,这无疑限制了我的实现方式。
编辑:嗯,似乎[io.file]位中的第三个参数的“读取”不起作用。 我现在有这个
$path = '\\Px\Support\Px Tools\Resources\jobs.xml'
foreach ($i in 1..10) {
$sleepTime = get-random -minimum:2 -maximum:5
$file = [IO.File]::Open($path, 'Open', 'ReadWrite', 'Read')
[xml]$xml = Get-Content $path
$newNode = $xml.createElement('Item')
$newNode.InnerXml = "$id : $i : $sleepTime : $(Get-Date)"
$xml.DocumentElement.AppendChild($newNode) > $null
$xml.Save($path)
$file.Close()
}
从理论上讲,应该使用我拥有的XML,带有两个虚拟日志项,读取它,附加另一个日志项(具有ID,迭代,睡眠时间和时间戳),并重复10次,并进行随机睡眠之间。它用
大便试图节省"The process cannot access the file '\\Px\Support\Px Tools\Resources\jobs.xml' because it is being used by another process."
我真的是在尝试做1000年前没有做过的事情吗?
好的,根据评论,这是我的位置。我想确保在进行处理时不能(轻松)手动编辑原稿。因此,我已经实现了这一点。 1:查找前哨文件,如果找不到 2:锁定原始文件,使其无法修改 3:将原件复制为前哨文件 4:根据需要修改前哨文件 5:解锁原件 6:将前哨文件复制到原始文件上 7:删除前哨
对我来说,似乎有些难为情,就像有人在解锁原件和复制哨兵之间手动修改原件一样,这种可能性很小。但是,似乎应该有一种可以100%确定性地处理此问题的方法,而且我想不出有没有哨兵文件的方法。
答案 0 :(得分:1)
一般情况下:文件并没有像数据库那样针对并发访问进行优化,因此,如果您需要一些复杂的并发访问,则需要自己动手。
This answer 到一个密切相关的问题,证明了使用单独的锁定文件 (前哨文件)来管理< strong>最小干扰。
但是,如果愿意为文件添加排他锁,可以简化方法,并不需要锁文件。 整个持续时间,进行读取,修改并保存修改。
相比之下,锁定文件方法允许读取和准备与其他进程读取同时进行的修改,并且仅需要排他锁才能进行实际的重写/替换文件。
但是,使用这两种方法时,都需要对文件进行排他锁定,以防止读者在重写文件时从文件中读取文件。
也就是说,您仍然需要所有相关流程的合作:
作家需要处理(暂时)无法完全打开文件的问题,即在其他进程(读者或作家)正在使用它时。
类似地,必须准备好 readers 来处理(暂时)无法打开文件(由作家更新文件时)的问题。
关键在于:
以文件共享模式None
打开文件(即在打开文件时拒绝其他进程使用同一文件),并保持打开状态直到更新完成。从跨进程的角度来看,这确保了操作是原子的。
仅使用{em> FileStream
返回的[System.IO.File]::Open()
实例来读取和写入文件(调用cmdlet或.NET方法,例如{{1} }会失败,因为他们自己将尝试打开-然后被锁定的文件)。
这是实现独占锁定的代码的固定版本:
System.Xml.XmlDocument.Save()
关于您尝试过的事情:
$path = '\\Px\Support\Px Tools\Resources\jobs.xml'
foreach ($i in 1..10) {
$sleepTime = get-random -minimum:2 -maximum:5
# Open the file with an exclusive lock so that no other process will be
# be able to even read it while an update is being performed.
# Use a RETRY LOOP until exclusive locking succeeds.
# You'll need a similar loop for *readers*.
# Note: In production code, you should also implement a TIMEOUT.
do { # retry loop
try {
$file = [IO.File]::Open($path, 'Open', 'ReadWrite', 'None')
} catch {
# Did opening fail due to the file being LOCKED? -> keep trying.
if ($_.Exception.InnerException -is [System.IO.IOException] -and ($_.Exception.InnerException.HResult -band 0x21) -in 0x21, 0x20) {
$host.ui.Write('.') # Some visual feedback
Start-Sleep -Milliseconds 500 # Sleep a little.
continue # Try again.
}
Throw # Unexpexted error -> rethrow.
}
break # Opening with exclusive lock succeeded, proceed below.
} while ($true)
# Read the file's content into an XML document (DOM).
$xml = New-Object xml # xml is a type accelerator for System.XML.XMLDocument
$xml.Load($file)
# Modify the XML document.
$newNode = $xml.createElement('Item')
$newNode.InnerXml = "$id : $i : $sleepTime : $(Get-Date)"
$null = $xml.DocumentElement.AppendChild($newNode)
# Convert the XML document back to a string
# and write that string back to the file.
$file.SetLength(0) # truncate existing content first
$xml.Save($file)
# Close the file and release the lock.
$file.Close()
}
以允许其他进程读取访问,但不允许写入访问的方式打开文件。
然后在$file = [IO.File]::Open($path, 'Open', 'ReadWrite', 'Read')
仍处于打开状态时调用$xml.Save($path)
,但是该方法调用(它本身也会尝试打开文件)需要 write 访问,但失败。 / p>
如上所示,关键是使用相同的$file
(FileStream
实例),该实例仅用于打开文件以更新文件。
还请注意,在$file
之前调用$file.Close()
不是 解决方案,因为这引入了竞争条件,其中另一个进程可以在时间内打开文件在这两个语句之间。