【问题标题】:Inter process Reader Writer lock (or file handles and access denied)进程间读写器锁定(或文件句柄和访问被拒绝)
【发布时间】:2009-05-08 17:17:49
【问题描述】:

好的,先介绍一下背景。我们需要一个进程间读/写锁。我们决定使用一个文件并使用 LockEx 和 UnlockEx 锁定第一个字节。该类在创建时会在系统临时文件夹中创建一个文件。该文件创建时具有读写访问权限并共享读|写|删除。我们还指定了 DeleteOnClose,这样我们就不会留下大量的临时文件。显然,AcquireReader 和 AcquireWriter 使用适当的标志调用 LockEx,而 ReleaseLock 调用 UnlockEx。
我们已经使用一个可以运行多个实例的小型应用程序测试了这个类,并且它运行良好。使用它的应用程序有问题,我们已设法在另一个小型测试应用程序中重新生成。在伪代码中是

创建 InterProcessReaderWriter 在不获取任何锁的情况下处理 InterProcessReaderWriter 启动一个需要读卡器锁的子进程

第一次运行时,它运行良好。如果您尝试再次运行它,而第一次的子进程仍然持有锁,我们会在尝试打开文件时收到 UnauthorisedAccessException。
这似乎是一个权限问题,而不是共享冲突,但此测试用例中的所有进程都以同一用户身份运行。这里有人有什么想法吗?

我注意到另一个问题,它建议使用互斥锁和信号量来实现我们想要的。我可能会改变我们的实现,但我仍然想知道是什么导致了这个问题。

【问题讨论】:

    标签: vb.net file locking file-security


    【解决方案1】:

    在我看来,子进程试图访问同一个文件两次。

    临时文件名是否唯一?

    【讨论】:

    • 我们正在尝试访问同一个文件。我们正在尝试实现类似于 access 如何处理其 .ldb 文件的东西。但每个文件只有一个锁,如果是第一个字节。
    【解决方案2】:

    为什么不锁定整个文件?而不是第一个字节。下面的示例类的好处是它可以跨不同机器上的进程工作,而不是仅限于一台机器。

    下面是一个使用 lockfilehelper 类的简单示例。

    Module Module1
        Sub Main()
            Using lockFile As New LockFileHelper("\\sharedfolder\simplefile.lock")
                If lockFile.LockAcquire(1000) Then
                    ' Do your work here.
                Else
                    ' Manage timeouts here.
                End If
            End Using
        End Sub
    End Module
    

    这是 Helper 类的代码。

    Public Class LockFileHelper
        Implements IDisposable
        '-------------------------------------------------------------------------------------------------
        ' We use lock files in various places in the system to provide a simple co-ordination mechanism
        ' between different threads within a process and for sharing access to resources with the same
        ' process running across different machines.
        '-------------------------------------------------------------------------------------------------
        Private _lockFileName As String
        Private _ioStream As IO.FileStream
        Private _acquiredLock As Boolean = False
        Private _wasLocked As Boolean = False
        Public Sub New(ByVal LockFileName As String)
            _lockFileName = LockFileName
        End Sub
        Public ReadOnly Property LockFileName() As String
            Get
                Return _lockFileName
            End Get
        End Property
        Public ReadOnly Property WasLocked() As Boolean
            Get
                Return _wasLocked
            End Get
        End Property
        Public Function Exists() As Boolean
            Return IO.File.Exists(_lockFileName)
        End Function
        Public Function IsLocked() As Boolean
            '-------------------------------------------------------------------------------------------------
            ' If this file already locked?
            '-------------------------------------------------------------------------------------------------
            Dim Result As Boolean = False
            Try
                _ioStream = IO.File.Open(_lockFileName, IO.FileMode.Create, IO.FileAccess.ReadWrite, IO.FileShare.None)
                _ioStream.Close()
            Catch ex As System.IO.IOException
                ' File is in used by another process.
                Result = True
            Catch ex As Exception
                Throw ex
            End Try
    
            Return Result
        End Function
        Public Sub LockAcquireWithException(ByVal TimeOutMilliseconds As Int32)
            If Not LockAcquire(TimeOutMilliseconds) Then
                Throw New Exception("Timed out trying to acquire a lock on the file " & _lockFileName)
            End If
        End Sub
        Public Function LockAcquire(ByVal TimeOutMilliseconds As Int32) As Boolean
            '-------------------------------------------------------------------------------------------------
            ' See have we already acquired the lock. THis can be useful in situations where we are passing
            ' locks around to various processes and each process may want to be sure it has acquired the lock.
            '-------------------------------------------------------------------------------------------------
            If _acquiredLock Then
                Return _acquiredLock
            End If
    
            _wasLocked = False
            Dim StartTicks As Int32 = System.Environment.TickCount
            Dim TimedOut As Boolean = False
            If Not IO.Directory.Exists(IO.Path.GetDirectoryName(_lockFileName)) Then
                IO.Directory.CreateDirectory(IO.Path.GetDirectoryName(_lockFileName))
            End If
            Do
                Try
                    _ioStream = IO.File.Open(_lockFileName, IO.FileMode.Create, IO.FileAccess.ReadWrite, IO.FileShare.None)
                    _acquiredLock = True
                Catch ex As System.IO.IOException
                    ' File is in used by another process.
                    _wasLocked = True
                    Threading.Thread.Sleep(100)
                Catch ex As Exception
                    Throw ex
                End Try
                TimedOut = ((System.Environment.TickCount - StartTicks) >= TimeOutMilliseconds)
            Loop Until _acquiredLock OrElse TimedOut
            '-------------------------------------------------------------------------------------------------
            ' Return back the status of the lock acquisition.
            '-------------------------------------------------------------------------------------------------
            Return _acquiredLock
        End Function
        Public Sub LockRelease()
            '-------------------------------------------------------------------------------------------------
            ' Release the lock (if we got it in the first place)
            '-------------------------------------------------------------------------------------------------
            If _acquiredLock Then
                _acquiredLock = False
                If Not IsNothing(_ioStream) Then
                    _ioStream.Close()
                    _ioStream = Nothing
                End If
            End If
        End Sub
        Private disposedValue As Boolean = False        ' To detect redundant calls
        ' IDisposable
        Protected Overridable Sub Dispose(ByVal disposing As Boolean)
            If Not Me.disposedValue Then
                If disposing Then
                    Call LockRelease()
                End If
    
                ' TODO: free shared unmanaged resources
            End If
            Me.disposedValue = True
        End Sub
    #Region " IDisposable Support "
        ' This code added by Visual Basic to correctly implement the disposable pattern.
        Public Sub Dispose() Implements IDisposable.Dispose
            ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
            Dispose(True)
            GC.SuppressFinalize(Me)
        End Sub
    #End Region
    End Class
    

    【讨论】:

    • 我们最初是为了锁定文件中的一个范围,因为它允许我们使用 DeleteOnClose。当您不得不关闭文件以更改锁定级别时,我不确定这将如何工作。
    猜你喜欢
    • 2023-03-27
    • 2010-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多