代码之家  ›  专栏  ›  技术社区  ›  Mike Christiansen

.NET(PowerShell)并发文件使用情况(锁定,同时仍允许读取访问)

  •  1
  • Mike Christiansen  · 技术社区  · 7 年前

    不要 需要并发写,但我 做 需要并发读取。读取/修改此XML文件的指定方法是使用 动力壳 脚本,使用 Linq到XML(XElement) 使用XML文件。

    理想情况是:

    1. 在整个工作日,团队中的任何用户都将输入预定义的命令,这些命令将查询XML文件以收集数据。每次用户运行其中一个查询时,它都会打开XML文件,执行查询,关闭XML文件,然后显示结果。
    2. 有时,在一天中,这些用户中的任何一个都需要修改XML文件。用户将输入预定义的命令,指定要修改的节点,然后输入要添加/修改的所有数据。然后,脚本将打开该文件(锁定它,这样其他用户就不能以读/写方式打开它),将XML文件读入 XElement 返回文件,然后释放锁。
    3. 在任何时候,当用户修改文件(某些操作可能很长)时,试图进行修改的任何其他用户 不能 能够打开文件(我将捕获异常,并显示“请稍等片刻”类型的消息)。但是,他们 必须

    我见过 this related post, 'Concurrent file usage in C#' The process cannot access the file 'C:\Path\To\file.xml' because it is being used by another process.

    using namespace System.Linq.Xml
    using namespace System.IO
    Add-Type -AssemblyName 'System.Xml.Linq (rest of Assembly's Full Name)'
    
    $filename = "C:\Path\To\file.xml"
    
    function Read-Only()
    {
        $filestream = [FileStream]::new($filename, [FileMode]::Open, [FileAccess]::Read)
        $database = [XElement]::Load($filestream)
        foreach($item in $database.Element("Items").Elements("Item"))
        {
            Write-Host "Item name $($item.Attribute("Name").Value)"
        }
        $filestream.Close()
        $filestream.Dispose()
    }
    function Read-Write()
    {
        $filestream = [FileStream]::new($filename, [FileMode]::Open, [FileAccess]::ReadWrite, [FileShare]::Read)
        $database = [XElement]::Load($filestream)
    
        $itemname = Read-Host "Enter an item name ('quit' to quit)"
        while($itemname -ne 'quit')
        {
            $database.Element("Items").Add([XElement]::new([XName]"Item", [XAttribute]::new([XName]"Name", $itemname)))
            $itemname = Read-Host "Enter an item name ('quit' to quit)"
        }
    
        $filestream.Seek(0, [SeekOrigin]::Begin)
        $database.Save($filestream)
    
        $filestream.Close()
        $filestream.Dispose()
    }
    

    如何锁定文件进行独占编辑,同时仍允许对任何其他客户端进行只读访问?

    我已经通过使用mklement0建议的解决方案解决了这个问题—使用一个单独的文件作为锁文件。代码张贴在此处: https://pastebin.com/ytzGE7se

    3 回复  |  直到 7 年前
        1
  •  1
  •   mklement0    7 年前

    如果你使用 [FileAccess]::Read 如果不显式指定文件共享模式,则如果文件已打开,则只有最初使用文件共享模式打开时,才能再次打开它 [FileShare]::ReadWrite (尽管你只是要求 阅读 访问,方法 默认值 写 你的 Read-Write [FileShare]::Read .

    如果你愿意,你眼前的问题就会消失 使用文件共享模式显式打开只读文件流 :

    [System.IO.FileStream]::new(
      $path, 
      [System.IO.FileMode]::Open, 
      [System.IO.FileAccess]::Read, 
      [System.IO.FileShare]::ReadWrite  # !! required
    )
    

    这允许其他两个读卡器同时访问,也允许唯一一个(read+)写卡器(可能先打开了文件)。

    然而, 获取文件 阅读时重写 ,所以 实现稳健和可预测的操作 不同的方法

    注: This answer 一个相关的问题表明 下面的解决方案, 需要更长时间


    在文件中进行修改 你的档案 ,然后替换原来的。

    这个 需要显式同步来协调可能的 更新程序 为了 序列化 更新
    你可以通过 单独的锁文件 (哨兵档案)比如说, updating

    Mike Christiansen(OP本人)最后还将锁定用户的用户名存储在该文件中,以便向其他可能的储物柜提供反馈。

    修改 请求:

    • 在循环中不断尝试,直到创建锁文件 成功,如果文件已存在则失败。

      • (重新)-创建 文件向其他可能的写入程序发出信号,表明已开始修改。相比之下,读者此时可以继续阅读。
    • 创建(临时) 复制 并在其中执行修改。

      • 迈克自己最后只是修改了一个 在记忆中 文件的副本,这简化了问题(如果您有足够的内存来读取整个文件)。
    • 用修改后的副本替换原始文件-使用重试循环,直到复制/重写原始文件成功,因为其他读取器可能会暂时阻止删除(重新创建)/重写原始文件。

    • 删除文件 更新

      • 确保文件是 总是 try .. finally ),因为让它逗留会阻止将来的更新;您可能还需要一种基于超时的机制,该机制基于等待先前存在的文件最终被删除 删除,如果可以假定先前的更新程序已崩溃。

    至于 阅读

    • 虽然没有进行任何修改,但并发读访问应该可以正常工作。

    • 当XML文件被临时拷贝/重写替换时,打开该文件进行读取将失败 在文件复制操作期间 /写操作,所以您也需要一个重试循环。


    迈克最终使用的代码可以找到 here .

        2
  •  1
  •   Jamie    7 年前

    here

    function Read-Only()
    {
        $database = [xml](Get-Content $filename)
        foreach($item in $database.Items)
        {
            Write-Host "Item name $item.Name"
        }
     }
    

    function Read-Write()
    {
        $database = [xml](Get-Content $filename)
    
        $itemname = Read-Host "Enter an item name ('quit' to quit)"
        while($itemname -ne 'quit')
        {
            $newItem = $database.CreateElement("item")
            $newItem.SetAttribute("Name", $itemname)
            $database.Items.AppendChild($newItem)
            # You can also save all the changes up and write once.
            $database.Save($filename)
            $itemname = Read-Host "Enter an item name ('quit' to quit)"
        }
    }
    
        3
  •  1
  •   Mike Twc    7 年前

    也许这能帮上忙。创建文件流时,有一个FileShare选项。如果将其设置为ReadWrite,则多个进程可以打开该文件

    $fsMain = [System.IO.File]::Open("C:\stack\out.txt", "Open", "ReadWrite", "ReadWrite")
    $fsReadOnly = [System.IO.File]::Open("C:\stack\out.txt", "Open", "Read", "ReadWrite")
    
    Write-Host ("fsMain:  CanRead=" + $fsMain.CanRead + ", CanWrite=" + $fsMain.CanWrite)
    Write-Host ("fsReadOnly:  CanRead= " + $fsReadOnly.CanRead + ", CanWrite=" + $fsReadOnly.CanWrite)
    
    $fsMain.Close()
    $fsReadOnly.Close()
    
    推荐文章